Statistics
| Branch: | Tag: | Revision:

root / pithos / api / functions.py @ 3d13f97a

History | View | Annotate | Download (49.2 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 logging
35 b956618e Antony Chazapis
import hashlib
36 b956618e Antony Chazapis
37 486b2dc2 Giorgos Verigakis
from django.conf import settings
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 88283e9e Antony Chazapis
from django.utils.encoding import smart_str
43 32a437b1 Sofia Papagiannaki
from xml.dom import minidom
44 b956618e Antony Chazapis
45 1713c946 Antony Chazapis
from pithos.lib.filter import parse_filters
46 1713c946 Antony Chazapis
47 297513ba Antony Chazapis
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound, Conflict,
48 5df6c6d1 Antony Chazapis
    LengthRequired, PreconditionFailed, RequestEntityTooLarge, RangeNotSatisfiable, UnprocessableEntity)
49 804e8fe7 Antony Chazapis
from pithos.api.util import (rename_meta_key, format_header_key, printable_header_dict, get_account_headers,
50 02c0c3fa Antony Chazapis
    put_account_headers, get_container_headers, put_container_headers, get_object_headers, put_object_headers,
51 e0f916bb Antony Chazapis
    update_manifest_meta, update_sharing_meta, update_public_meta, validate_modification_preconditions,
52 3436eeb0 Antony Chazapis
    validate_matching_preconditions, split_container_object_string, copy_or_move_object,
53 817890f2 Antony Chazapis
    get_int_parameter, get_content_length, get_content_range, socket_read_iterator, SaveToBackendHandler,
54 c48acbfd Sofia Papagiannaki
    object_data_response, put_object_block, hashmap_hash, api_method, json_encode_decimal)
55 5df6c6d1 Antony Chazapis
from pithos.backends.base import NotAllowedError, QuotaError
56 b956618e Antony Chazapis
57 b956618e Antony Chazapis
58 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
59 b956618e Antony Chazapis
60 b956618e Antony Chazapis
61 b956618e Antony Chazapis
def top_demux(request):
62 b956618e Antony Chazapis
    if request.method == 'GET':
63 a70c5269 Antony Chazapis
        if getattr(request, 'user', None) is not None:
64 f6c97079 Antony Chazapis
            return account_list(request)
65 b956618e Antony Chazapis
        return authenticate(request)
66 b956618e Antony Chazapis
    else:
67 b956618e Antony Chazapis
        return method_not_allowed(request)
68 b956618e Antony Chazapis
69 b956618e Antony Chazapis
def account_demux(request, v_account):
70 b956618e Antony Chazapis
    if request.method == 'HEAD':
71 b956618e Antony Chazapis
        return account_meta(request, v_account)
72 b956618e Antony Chazapis
    elif request.method == 'POST':
73 b956618e Antony Chazapis
        return account_update(request, v_account)
74 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
75 83dd59c5 Antony Chazapis
        return container_list(request, v_account)
76 b956618e Antony Chazapis
    else:
77 b956618e Antony Chazapis
        return method_not_allowed(request)
78 b956618e Antony Chazapis
79 b956618e Antony Chazapis
def container_demux(request, v_account, v_container):
80 b956618e Antony Chazapis
    if request.method == 'HEAD':
81 b956618e Antony Chazapis
        return container_meta(request, v_account, v_container)
82 b956618e Antony Chazapis
    elif request.method == 'PUT':
83 b956618e Antony Chazapis
        return container_create(request, v_account, v_container)
84 b956618e Antony Chazapis
    elif request.method == 'POST':
85 b956618e Antony Chazapis
        return container_update(request, v_account, v_container)
86 b956618e Antony Chazapis
    elif request.method == 'DELETE':
87 b956618e Antony Chazapis
        return container_delete(request, v_account, v_container)
88 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
89 83dd59c5 Antony Chazapis
        return object_list(request, v_account, v_container)
90 b956618e Antony Chazapis
    else:
91 b956618e Antony Chazapis
        return method_not_allowed(request)
92 b956618e Antony Chazapis
93 b956618e Antony Chazapis
def object_demux(request, v_account, v_container, v_object):
94 b956618e Antony Chazapis
    if request.method == 'HEAD':
95 b956618e Antony Chazapis
        return object_meta(request, v_account, v_container, v_object)
96 b956618e Antony Chazapis
    elif request.method == 'GET':
97 b956618e Antony Chazapis
        return object_read(request, v_account, v_container, v_object)
98 b956618e Antony Chazapis
    elif request.method == 'PUT':
99 b956618e Antony Chazapis
        return object_write(request, v_account, v_container, v_object)
100 b956618e Antony Chazapis
    elif request.method == 'COPY':
101 b956618e Antony Chazapis
        return object_copy(request, v_account, v_container, v_object)
102 b956618e Antony Chazapis
    elif request.method == 'MOVE':
103 b956618e Antony Chazapis
        return object_move(request, v_account, v_container, v_object)
104 b956618e Antony Chazapis
    elif request.method == 'POST':
105 1d5c57d3 Antony Chazapis
        if request.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'):
106 1d5c57d3 Antony Chazapis
            return object_write_form(request, v_account, v_container, v_object)
107 b956618e Antony Chazapis
        return object_update(request, v_account, v_container, v_object)
108 b956618e Antony Chazapis
    elif request.method == 'DELETE':
109 b956618e Antony Chazapis
        return object_delete(request, v_account, v_container, v_object)
110 b956618e Antony Chazapis
    else:
111 b956618e Antony Chazapis
        return method_not_allowed(request)
112 b956618e Antony Chazapis
113 a70c5269 Antony Chazapis
@api_method('GET', user_required=False)
114 b956618e Antony Chazapis
def authenticate(request):
115 b956618e Antony Chazapis
    # Normal Response Codes: 204
116 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
117 297513ba Antony Chazapis
    #                       forbidden (403),
118 b956618e Antony Chazapis
    #                       badRequest (400)
119 b956618e Antony Chazapis
    
120 b956618e Antony Chazapis
    x_auth_user = request.META.get('HTTP_X_AUTH_USER')
121 b956618e Antony Chazapis
    x_auth_key = request.META.get('HTTP_X_AUTH_KEY')
122 b956618e Antony Chazapis
    if not x_auth_user or not x_auth_key:
123 b956618e Antony Chazapis
        raise BadRequest('Missing X-Auth-User or X-Auth-Key header')
124 b956618e Antony Chazapis
    response = HttpResponse(status=204)
125 f7667baf Antony Chazapis
    
126 f7667baf Antony Chazapis
    uri = request.build_absolute_uri()
127 f7667baf Antony Chazapis
    if '?' in uri:
128 f7667baf Antony Chazapis
        uri = uri[:uri.find('?')]
129 f7667baf Antony Chazapis
    
130 36d368f7 Antony Chazapis
    response['X-Auth-Token'] = x_auth_key
131 af6d3b5d Antony Chazapis
    response['X-Storage-Url'] = uri + ('' if uri.endswith('/') else '/') + x_auth_user
132 b956618e Antony Chazapis
    return response
133 b956618e Antony Chazapis
134 f6c97079 Antony Chazapis
@api_method('GET', format_allowed=True)
135 f6c97079 Antony Chazapis
def account_list(request):
136 f6c97079 Antony Chazapis
    # Normal Response Codes: 200, 204
137 f6c97079 Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
138 f6c97079 Antony Chazapis
    #                       badRequest (400)
139 f6c97079 Antony Chazapis
    
140 f6c97079 Antony Chazapis
    response = HttpResponse()
141 f6c97079 Antony Chazapis
    
142 f6c97079 Antony Chazapis
    marker = request.GET.get('marker')
143 f6c97079 Antony Chazapis
    limit = get_int_parameter(request.GET.get('limit'))
144 f6c97079 Antony Chazapis
    if not limit:
145 f6c97079 Antony Chazapis
        limit = 10000
146 f6c97079 Antony Chazapis
    
147 61efb530 Antony Chazapis
    accounts = request.backend.list_accounts(request.user_uniq, marker, limit)
148 f6c97079 Antony Chazapis
    
149 f6c97079 Antony Chazapis
    if request.serialization == 'text':
150 f6c97079 Antony Chazapis
        if len(accounts) == 0:
151 f6c97079 Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
152 f6c97079 Antony Chazapis
            response.status_code = 204
153 f6c97079 Antony Chazapis
            return response
154 f6c97079 Antony Chazapis
        response.status_code = 200
155 f6c97079 Antony Chazapis
        response.content = '\n'.join(accounts) + '\n'
156 f6c97079 Antony Chazapis
        return response
157 f6c97079 Antony Chazapis
    
158 f6c97079 Antony Chazapis
    account_meta = []
159 f6c97079 Antony Chazapis
    for x in accounts:
160 690747fe Antony Chazapis
        if x == request.user_uniq:
161 690747fe Antony Chazapis
            continue
162 f6c97079 Antony Chazapis
        try:
163 61efb530 Antony Chazapis
            meta = request.backend.get_account_meta(request.user_uniq, x)
164 61efb530 Antony Chazapis
            groups = request.backend.get_account_groups(request.user_uniq, x)
165 f6c97079 Antony Chazapis
        except NotAllowedError:
166 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
167 f6c97079 Antony Chazapis
        else:
168 804e8fe7 Antony Chazapis
            rename_meta_key(meta, 'modified', 'last_modified')
169 804e8fe7 Antony Chazapis
            rename_meta_key(meta, 'until_timestamp', 'x_account_until_timestamp')
170 690747fe Antony Chazapis
            m = dict([(k[15:], v) for k, v in meta.iteritems() if k.startswith('X-Account-Meta-')])
171 690747fe Antony Chazapis
            for k in m:
172 690747fe Antony Chazapis
                del(meta['X-Account-Meta-' + k])
173 690747fe Antony Chazapis
            if m:
174 690747fe Antony Chazapis
                meta['X-Account-Meta'] = printable_header_dict(m)
175 690747fe Antony Chazapis
            if groups:
176 690747fe Antony Chazapis
                meta['X-Account-Group'] = printable_header_dict(dict([(k, ','.join(v)) for k, v in groups.iteritems()]))
177 f6c97079 Antony Chazapis
            account_meta.append(printable_header_dict(meta))
178 f6c97079 Antony Chazapis
    if request.serialization == 'xml':
179 f6c97079 Antony Chazapis
        data = render_to_string('accounts.xml', {'accounts': account_meta})
180 f6c97079 Antony Chazapis
    elif request.serialization  == 'json':
181 f6c97079 Antony Chazapis
        data = json.dumps(account_meta)
182 f6c97079 Antony Chazapis
    response.status_code = 200
183 f6c97079 Antony Chazapis
    response.content = data
184 f6c97079 Antony Chazapis
    return response
185 f6c97079 Antony Chazapis
186 b956618e Antony Chazapis
@api_method('HEAD')
187 b956618e Antony Chazapis
def account_meta(request, v_account):
188 b956618e Antony Chazapis
    # Normal Response Codes: 204
189 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
190 297513ba Antony Chazapis
    #                       forbidden (403),
191 b956618e Antony Chazapis
    #                       badRequest (400)
192 b956618e Antony Chazapis
    
193 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
194 cca6c617 Antony Chazapis
    try:
195 61efb530 Antony Chazapis
        meta = request.backend.get_account_meta(request.user_uniq, v_account, until)
196 61efb530 Antony Chazapis
        groups = request.backend.get_account_groups(request.user_uniq, v_account)
197 61efb530 Antony Chazapis
        policy = request.backend.get_account_policy(request.user_uniq, v_account)
198 cca6c617 Antony Chazapis
    except NotAllowedError:
199 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
200 b956618e Antony Chazapis
    
201 a8326bef Antony Chazapis
    validate_modification_preconditions(request, meta)
202 a8326bef Antony Chazapis
    
203 b956618e Antony Chazapis
    response = HttpResponse(status=204)
204 647a5f48 Antony Chazapis
    put_account_headers(response, meta, groups, policy)
205 b956618e Antony Chazapis
    return response
206 b956618e Antony Chazapis
207 b956618e Antony Chazapis
@api_method('POST')
208 b956618e Antony Chazapis
def account_update(request, v_account):
209 b956618e Antony Chazapis
    # Normal Response Codes: 202
210 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
211 297513ba Antony Chazapis
    #                       forbidden (403),
212 b956618e Antony Chazapis
    #                       badRequest (400)
213 b956618e Antony Chazapis
    
214 02c0c3fa Antony Chazapis
    meta, groups = get_account_headers(request)
215 a6eb13e9 Antony Chazapis
    replace = True
216 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
217 b18ef3ad Antony Chazapis
        replace = False
218 02c0c3fa Antony Chazapis
    if groups:
219 02c0c3fa Antony Chazapis
        try:
220 61efb530 Antony Chazapis
            request.backend.update_account_groups(request.user_uniq, v_account,
221 39593b2b Giorgos Verigakis
                                                    groups, replace)
222 02c0c3fa Antony Chazapis
        except NotAllowedError:
223 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
224 02c0c3fa Antony Chazapis
        except ValueError:
225 02c0c3fa Antony Chazapis
            raise BadRequest('Invalid groups header')
226 77edd23d Antony Chazapis
    if meta or replace:
227 77edd23d Antony Chazapis
        try:
228 61efb530 Antony Chazapis
            request.backend.update_account_meta(request.user_uniq, v_account, meta,
229 39593b2b Giorgos Verigakis
                                                replace)
230 77edd23d Antony Chazapis
        except NotAllowedError:
231 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
232 b956618e Antony Chazapis
    return HttpResponse(status=202)
233 b956618e Antony Chazapis
234 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
235 b956618e Antony Chazapis
def container_list(request, v_account):
236 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
237 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
238 b956618e Antony Chazapis
    #                       itemNotFound (404),
239 297513ba Antony Chazapis
    #                       forbidden (403),
240 b956618e Antony Chazapis
    #                       badRequest (400)
241 b956618e Antony Chazapis
    
242 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
243 cca6c617 Antony Chazapis
    try:
244 61efb530 Antony Chazapis
        meta = request.backend.get_account_meta(request.user_uniq, v_account, until)
245 61efb530 Antony Chazapis
        groups = request.backend.get_account_groups(request.user_uniq, v_account)
246 61efb530 Antony Chazapis
        policy = request.backend.get_account_policy(request.user_uniq, v_account)
247 cca6c617 Antony Chazapis
    except NotAllowedError:
248 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
249 b956618e Antony Chazapis
    
250 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
251 b956618e Antony Chazapis
    
252 b956618e Antony Chazapis
    response = HttpResponse()
253 647a5f48 Antony Chazapis
    put_account_headers(response, meta, groups, policy)
254 b956618e Antony Chazapis
    
255 b956618e Antony Chazapis
    marker = request.GET.get('marker')
256 f6c97079 Antony Chazapis
    limit = get_int_parameter(request.GET.get('limit'))
257 f6c97079 Antony Chazapis
    if not limit:
258 f6c97079 Antony Chazapis
        limit = 10000
259 b956618e Antony Chazapis
    
260 b18ef3ad Antony Chazapis
    shared = False
261 b18ef3ad Antony Chazapis
    if 'shared' in request.GET:
262 b18ef3ad Antony Chazapis
        shared = True
263 b18ef3ad Antony Chazapis
    
264 b956618e Antony Chazapis
    try:
265 61efb530 Antony Chazapis
        containers = request.backend.list_containers(request.user_uniq, v_account,
266 39593b2b Giorgos Verigakis
                                                marker, limit, shared, until)
267 cca6c617 Antony Chazapis
    except NotAllowedError:
268 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
269 b956618e Antony Chazapis
    except NameError:
270 b956618e Antony Chazapis
        containers = []
271 b956618e Antony Chazapis
    
272 b956618e Antony Chazapis
    if request.serialization == 'text':
273 b956618e Antony Chazapis
        if len(containers) == 0:
274 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
275 b956618e Antony Chazapis
            response.status_code = 204
276 b956618e Antony Chazapis
            return response
277 b956618e Antony Chazapis
        response.status_code = 200
278 f6c97079 Antony Chazapis
        response.content = '\n'.join(containers) + '\n'
279 b956618e Antony Chazapis
        return response
280 b956618e Antony Chazapis
    
281 b956618e Antony Chazapis
    container_meta = []
282 b956618e Antony Chazapis
    for x in containers:
283 f6c97079 Antony Chazapis
        try:
284 61efb530 Antony Chazapis
            meta = request.backend.get_container_meta(request.user_uniq, v_account,
285 39593b2b Giorgos Verigakis
                                                        x, until)
286 61efb530 Antony Chazapis
            policy = request.backend.get_container_policy(request.user_uniq,
287 39593b2b Giorgos Verigakis
                                                            v_account, x)
288 f6c97079 Antony Chazapis
        except NotAllowedError:
289 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
290 f6c97079 Antony Chazapis
        except NameError:
291 f6c97079 Antony Chazapis
            pass
292 f6c97079 Antony Chazapis
        else:
293 804e8fe7 Antony Chazapis
            rename_meta_key(meta, 'modified', 'last_modified')
294 804e8fe7 Antony Chazapis
            rename_meta_key(meta, 'until_timestamp', 'x_container_until_timestamp')
295 690747fe Antony Chazapis
            m = dict([(k[17:], v) for k, v in meta.iteritems() if k.startswith('X-Container-Meta-')])
296 690747fe Antony Chazapis
            for k in m:
297 690747fe Antony Chazapis
                del(meta['X-Container-Meta-' + k])
298 690747fe Antony Chazapis
            if m:
299 690747fe Antony Chazapis
                meta['X-Container-Meta'] = printable_header_dict(m)
300 690747fe Antony Chazapis
            if policy:
301 690747fe Antony Chazapis
                meta['X-Container-Policy'] = printable_header_dict(dict([(k, v) for k, v in policy.iteritems()]))
302 f6c97079 Antony Chazapis
            container_meta.append(printable_header_dict(meta))
303 b956618e Antony Chazapis
    if request.serialization == 'xml':
304 2c22e4ac Antony Chazapis
        data = render_to_string('containers.xml', {'account': v_account, 'containers': container_meta})
305 b956618e Antony Chazapis
    elif request.serialization  == 'json':
306 b956618e Antony Chazapis
        data = json.dumps(container_meta)
307 b956618e Antony Chazapis
    response.status_code = 200
308 b956618e Antony Chazapis
    response.content = data
309 b956618e Antony Chazapis
    return response
310 b956618e Antony Chazapis
311 b956618e Antony Chazapis
@api_method('HEAD')
312 b956618e Antony Chazapis
def container_meta(request, v_account, v_container):
313 b956618e Antony Chazapis
    # Normal Response Codes: 204
314 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
315 b956618e Antony Chazapis
    #                       itemNotFound (404),
316 297513ba Antony Chazapis
    #                       forbidden (403),
317 b956618e Antony Chazapis
    #                       badRequest (400)
318 b956618e Antony Chazapis
    
319 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
320 b956618e Antony Chazapis
    try:
321 61efb530 Antony Chazapis
        meta = request.backend.get_container_meta(request.user_uniq, v_account,
322 39593b2b Giorgos Verigakis
                                                    v_container, until)
323 61efb530 Antony Chazapis
        meta['object_meta'] = request.backend.list_object_meta(request.user_uniq,
324 39593b2b Giorgos Verigakis
                                                v_account, v_container, until)
325 61efb530 Antony Chazapis
        policy = request.backend.get_container_policy(request.user_uniq, v_account,
326 39593b2b Giorgos Verigakis
                                                        v_container)
327 cca6c617 Antony Chazapis
    except NotAllowedError:
328 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
329 b956618e Antony Chazapis
    except NameError:
330 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
331 b956618e Antony Chazapis
    
332 a8326bef Antony Chazapis
    validate_modification_preconditions(request, meta)
333 a8326bef Antony Chazapis
    
334 b956618e Antony Chazapis
    response = HttpResponse(status=204)
335 39593b2b Giorgos Verigakis
    put_container_headers(request, response, meta, policy)
336 b956618e Antony Chazapis
    return response
337 b956618e Antony Chazapis
338 b956618e Antony Chazapis
@api_method('PUT')
339 b956618e Antony Chazapis
def container_create(request, v_account, v_container):
340 b956618e Antony Chazapis
    # Normal Response Codes: 201, 202
341 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
342 b956618e Antony Chazapis
    #                       itemNotFound (404),
343 297513ba Antony Chazapis
    #                       forbidden (403),
344 b956618e Antony Chazapis
    #                       badRequest (400)
345 b956618e Antony Chazapis
    
346 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
347 b956618e Antony Chazapis
    
348 b956618e Antony Chazapis
    try:
349 61efb530 Antony Chazapis
        request.backend.put_container(request.user_uniq, v_account, v_container, policy)
350 b956618e Antony Chazapis
        ret = 201
351 cca6c617 Antony Chazapis
    except NotAllowedError:
352 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
353 a8326bef Antony Chazapis
    except ValueError:
354 a8326bef Antony Chazapis
        raise BadRequest('Invalid policy header')
355 b956618e Antony Chazapis
    except NameError:
356 b956618e Antony Chazapis
        ret = 202
357 b956618e Antony Chazapis
    
358 c5d308c6 Antony Chazapis
    if ret == 202 and policy:
359 cca6c617 Antony Chazapis
        try:
360 61efb530 Antony Chazapis
            request.backend.update_container_policy(request.user_uniq, v_account,
361 39593b2b Giorgos Verigakis
                                            v_container, policy, replace=False)
362 c5d308c6 Antony Chazapis
        except NotAllowedError:
363 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
364 c5d308c6 Antony Chazapis
        except NameError:
365 c5d308c6 Antony Chazapis
            raise ItemNotFound('Container does not exist')
366 c5d308c6 Antony Chazapis
        except ValueError:
367 c5d308c6 Antony Chazapis
            raise BadRequest('Invalid policy header')
368 c5d308c6 Antony Chazapis
    if meta:
369 c5d308c6 Antony Chazapis
        try:
370 61efb530 Antony Chazapis
            request.backend.update_container_meta(request.user_uniq, v_account,
371 39593b2b Giorgos Verigakis
                                            v_container, meta, replace=False)
372 cca6c617 Antony Chazapis
        except NotAllowedError:
373 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
374 cca6c617 Antony Chazapis
        except NameError:
375 cca6c617 Antony Chazapis
            raise ItemNotFound('Container does not exist')
376 b956618e Antony Chazapis
    
377 b956618e Antony Chazapis
    return HttpResponse(status=ret)
378 b956618e Antony Chazapis
379 b956618e Antony Chazapis
@api_method('POST')
380 b956618e Antony Chazapis
def container_update(request, v_account, v_container):
381 b956618e Antony Chazapis
    # Normal Response Codes: 202
382 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
383 b956618e Antony Chazapis
    #                       itemNotFound (404),
384 297513ba Antony Chazapis
    #                       forbidden (403),
385 b956618e Antony Chazapis
    #                       badRequest (400)
386 b956618e Antony Chazapis
    
387 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
388 a6eb13e9 Antony Chazapis
    replace = True
389 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
390 a6eb13e9 Antony Chazapis
        replace = False
391 3ab38c43 Antony Chazapis
    if policy:
392 3ab38c43 Antony Chazapis
        try:
393 61efb530 Antony Chazapis
            request.backend.update_container_policy(request.user_uniq, v_account,
394 39593b2b Giorgos Verigakis
                                                v_container, policy, replace)
395 3ab38c43 Antony Chazapis
        except NotAllowedError:
396 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
397 3ab38c43 Antony Chazapis
        except NameError:
398 3ab38c43 Antony Chazapis
            raise ItemNotFound('Container does not exist')
399 3ab38c43 Antony Chazapis
        except ValueError:
400 3ab38c43 Antony Chazapis
            raise BadRequest('Invalid policy header')
401 77edd23d Antony Chazapis
    if meta or replace:
402 77edd23d Antony Chazapis
        try:
403 61efb530 Antony Chazapis
            request.backend.update_container_meta(request.user_uniq, v_account,
404 39593b2b Giorgos Verigakis
                                                    v_container, meta, replace)
405 77edd23d Antony Chazapis
        except NotAllowedError:
406 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
407 77edd23d Antony Chazapis
        except NameError:
408 77edd23d Antony Chazapis
            raise ItemNotFound('Container does not exist')
409 5144b772 Antony Chazapis
    
410 5144b772 Antony Chazapis
    content_length = -1
411 5144b772 Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
412 5144b772 Antony Chazapis
        content_length = get_int_parameter(request.META.get('CONTENT_LENGTH', 0))
413 bd08f5c0 Antony Chazapis
    content_type = request.META.get('CONTENT_TYPE')
414 5144b772 Antony Chazapis
    hashmap = []
415 5144b772 Antony Chazapis
    if content_type and content_type == 'application/octet-stream' and content_length != 0:
416 5144b772 Antony Chazapis
        for data in socket_read_iterator(request, content_length,
417 5144b772 Antony Chazapis
                                            request.backend.block_size):
418 5144b772 Antony Chazapis
            # TODO: Raise 408 (Request Timeout) if this takes too long.
419 5144b772 Antony Chazapis
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
420 5144b772 Antony Chazapis
            hashmap.append(request.backend.put_block(data))
421 5144b772 Antony Chazapis
    
422 5144b772 Antony Chazapis
    response = HttpResponse(status=202)
423 5144b772 Antony Chazapis
    if hashmap:
424 5144b772 Antony Chazapis
        response.content = '\n'.join(hashmap) + '\n'
425 5144b772 Antony Chazapis
    return response
426 b956618e Antony Chazapis
427 b956618e Antony Chazapis
@api_method('DELETE')
428 b956618e Antony Chazapis
def container_delete(request, v_account, v_container):
429 b956618e Antony Chazapis
    # Normal Response Codes: 204
430 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
431 b956618e Antony Chazapis
    #                       conflict (409),
432 b956618e Antony Chazapis
    #                       itemNotFound (404),
433 297513ba Antony Chazapis
    #                       forbidden (403),
434 b956618e Antony Chazapis
    #                       badRequest (400)
435 b956618e Antony Chazapis
    
436 bbd20b55 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
437 b956618e Antony Chazapis
    try:
438 61efb530 Antony Chazapis
        request.backend.delete_container(request.user_uniq, v_account, v_container,
439 39593b2b Giorgos Verigakis
                                            until)
440 cca6c617 Antony Chazapis
    except NotAllowedError:
441 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
442 b956618e Antony Chazapis
    except NameError:
443 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
444 b956618e Antony Chazapis
    except IndexError:
445 b956618e Antony Chazapis
        raise Conflict('Container is not empty')
446 b956618e Antony Chazapis
    return HttpResponse(status=204)
447 b956618e Antony Chazapis
448 b956618e Antony Chazapis
@api_method('GET', format_allowed=True)
449 b956618e Antony Chazapis
def object_list(request, v_account, v_container):
450 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
451 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
452 b956618e Antony Chazapis
    #                       itemNotFound (404),
453 297513ba Antony Chazapis
    #                       forbidden (403),
454 b956618e Antony Chazapis
    #                       badRequest (400)
455 b956618e Antony Chazapis
    
456 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
457 b956618e Antony Chazapis
    try:
458 61efb530 Antony Chazapis
        meta = request.backend.get_container_meta(request.user_uniq, v_account,
459 39593b2b Giorgos Verigakis
                                                    v_container, until)
460 61efb530 Antony Chazapis
        meta['object_meta'] = request.backend.list_object_meta(request.user_uniq,
461 39593b2b Giorgos Verigakis
                                                v_account, v_container, until)
462 61efb530 Antony Chazapis
        policy = request.backend.get_container_policy(request.user_uniq, v_account,
463 39593b2b Giorgos Verigakis
                                                        v_container)
464 cca6c617 Antony Chazapis
    except NotAllowedError:
465 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
466 b956618e Antony Chazapis
    except NameError:
467 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
468 b956618e Antony Chazapis
    
469 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
470 b956618e Antony Chazapis
    
471 b956618e Antony Chazapis
    response = HttpResponse()
472 39593b2b Giorgos Verigakis
    put_container_headers(request, response, meta, policy)
473 b956618e Antony Chazapis
    
474 b956618e Antony Chazapis
    path = request.GET.get('path')
475 b956618e Antony Chazapis
    prefix = request.GET.get('prefix')
476 b956618e Antony Chazapis
    delimiter = request.GET.get('delimiter')
477 b956618e Antony Chazapis
    
478 b956618e Antony Chazapis
    # Path overrides prefix and delimiter.
479 b956618e Antony Chazapis
    virtual = True
480 b956618e Antony Chazapis
    if path:
481 b956618e Antony Chazapis
        prefix = path
482 b956618e Antony Chazapis
        delimiter = '/'
483 b956618e Antony Chazapis
        virtual = False
484 b956618e Antony Chazapis
    
485 b956618e Antony Chazapis
    # Naming policy.
486 b956618e Antony Chazapis
    if prefix and delimiter:
487 b956618e Antony Chazapis
        prefix = prefix + delimiter
488 b956618e Antony Chazapis
    if not prefix:
489 b956618e Antony Chazapis
        prefix = ''
490 b956618e Antony Chazapis
    prefix = prefix.lstrip('/')
491 b956618e Antony Chazapis
    
492 b956618e Antony Chazapis
    marker = request.GET.get('marker')
493 f6c97079 Antony Chazapis
    limit = get_int_parameter(request.GET.get('limit'))
494 f6c97079 Antony Chazapis
    if not limit:
495 f6c97079 Antony Chazapis
        limit = 10000
496 b956618e Antony Chazapis
    
497 22dab079 Antony Chazapis
    keys = request.GET.get('meta')
498 22dab079 Antony Chazapis
    if keys:
499 1713c946 Antony Chazapis
        keys = [smart_str(x.strip()) for x in keys.split(',') if x.strip() != '']
500 1713c946 Antony Chazapis
        included, excluded, opers = parse_filters(keys)
501 1713c946 Antony Chazapis
        keys = []
502 1713c946 Antony Chazapis
        keys += [format_header_key('X-Object-Meta-' + x) for x in included]
503 3d13f97a Sofia Papagiannaki
        keys += ['!'+format_header_key('X-Object-Meta-' + x) for x in excluded]
504 1713c946 Antony Chazapis
        keys += ['%s%s%s' % (format_header_key('X-Object-Meta-' + k), o, v) for k, o, v in opers]
505 22dab079 Antony Chazapis
    else:
506 22dab079 Antony Chazapis
        keys = []
507 22dab079 Antony Chazapis
    
508 b18ef3ad Antony Chazapis
    shared = False
509 b18ef3ad Antony Chazapis
    if 'shared' in request.GET:
510 b18ef3ad Antony Chazapis
        shared = True
511 b18ef3ad Antony Chazapis
    
512 b956618e Antony Chazapis
    try:
513 61efb530 Antony Chazapis
        objects = request.backend.list_objects(request.user_uniq, v_account,
514 39593b2b Giorgos Verigakis
                                    v_container, prefix, delimiter, marker,
515 39593b2b Giorgos Verigakis
                                    limit, virtual, keys, shared, until)
516 cca6c617 Antony Chazapis
    except NotAllowedError:
517 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
518 b956618e Antony Chazapis
    except NameError:
519 b956618e Antony Chazapis
        raise ItemNotFound('Container does not exist')
520 b956618e Antony Chazapis
    
521 b956618e Antony Chazapis
    if request.serialization == 'text':
522 b956618e Antony Chazapis
        if len(objects) == 0:
523 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
524 b956618e Antony Chazapis
            response.status_code = 204
525 b956618e Antony Chazapis
            return response
526 b956618e Antony Chazapis
        response.status_code = 200
527 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in objects]) + '\n'
528 b956618e Antony Chazapis
        return response
529 b956618e Antony Chazapis
    
530 b956618e Antony Chazapis
    object_meta = []
531 b956618e Antony Chazapis
    for x in objects:
532 83dd59c5 Antony Chazapis
        if x[1] is None:
533 b956618e Antony Chazapis
            # Virtual objects/directories.
534 83dd59c5 Antony Chazapis
            object_meta.append({'subdir': x[0]})
535 83dd59c5 Antony Chazapis
        else:
536 83dd59c5 Antony Chazapis
            try:
537 61efb530 Antony Chazapis
                meta = request.backend.get_object_meta(request.user_uniq, v_account,
538 39593b2b Giorgos Verigakis
                                                        v_container, x[0], x[1])
539 e8886082 Antony Chazapis
                if until is None:
540 39593b2b Giorgos Verigakis
                    permissions = request.backend.get_object_permissions(
541 61efb530 Antony Chazapis
                                    request.user_uniq, v_account, v_container, x[0])
542 61efb530 Antony Chazapis
                    public = request.backend.get_object_public(request.user_uniq,
543 39593b2b Giorgos Verigakis
                                                v_account, v_container, x[0])
544 e8886082 Antony Chazapis
                else:
545 e8886082 Antony Chazapis
                    permissions = None
546 e0f916bb Antony Chazapis
                    public = None
547 cca6c617 Antony Chazapis
            except NotAllowedError:
548 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
549 83dd59c5 Antony Chazapis
            except NameError:
550 83dd59c5 Antony Chazapis
                pass
551 038f1ae9 Antony Chazapis
            else:
552 4a1c29ea Antony Chazapis
                rename_meta_key(meta, 'hash', 'x_object_hash') # Will be replaced by ETag.
553 4a1c29ea Antony Chazapis
                rename_meta_key(meta, 'ETag', 'hash')
554 804e8fe7 Antony Chazapis
                rename_meta_key(meta, 'modified', 'last_modified')
555 804e8fe7 Antony Chazapis
                rename_meta_key(meta, 'modified_by', 'x_object_modified_by')
556 804e8fe7 Antony Chazapis
                rename_meta_key(meta, 'version', 'x_object_version')
557 804e8fe7 Antony Chazapis
                rename_meta_key(meta, 'version_timestamp', 'x_object_version_timestamp')
558 690747fe Antony Chazapis
                m = dict([(k[14:], v) for k, v in meta.iteritems() if k.startswith('X-Object-Meta-')])
559 690747fe Antony Chazapis
                for k in m:
560 690747fe Antony Chazapis
                    del(meta['X-Object-Meta-' + k])
561 690747fe Antony Chazapis
                if m:
562 690747fe Antony Chazapis
                    meta['X-Object-Meta'] = printable_header_dict(m)
563 067cf1fc Antony Chazapis
                update_sharing_meta(request, permissions, v_account, v_container, x[0], meta)
564 038f1ae9 Antony Chazapis
                update_public_meta(public, meta)
565 038f1ae9 Antony Chazapis
                object_meta.append(printable_header_dict(meta))
566 b956618e Antony Chazapis
    if request.serialization == 'xml':
567 b956618e Antony Chazapis
        data = render_to_string('objects.xml', {'container': v_container, 'objects': object_meta})
568 b956618e Antony Chazapis
    elif request.serialization  == 'json':
569 c48acbfd Sofia Papagiannaki
        data = json.dumps(object_meta, default=json_encode_decimal)
570 b956618e Antony Chazapis
    response.status_code = 200
571 b956618e Antony Chazapis
    response.content = data
572 b956618e Antony Chazapis
    return response
573 b956618e Antony Chazapis
574 b956618e Antony Chazapis
@api_method('HEAD')
575 b956618e Antony Chazapis
def object_meta(request, v_account, v_container, v_object):
576 b956618e Antony Chazapis
    # Normal Response Codes: 204
577 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
578 b956618e Antony Chazapis
    #                       itemNotFound (404),
579 297513ba Antony Chazapis
    #                       forbidden (403),
580 b956618e Antony Chazapis
    #                       badRequest (400)
581 b956618e Antony Chazapis
    
582 104626e3 Antony Chazapis
    version = request.GET.get('version')
583 b956618e Antony Chazapis
    try:
584 61efb530 Antony Chazapis
        meta = request.backend.get_object_meta(request.user_uniq, v_account,
585 39593b2b Giorgos Verigakis
                                                v_container, v_object, version)
586 e8886082 Antony Chazapis
        if version is None:
587 61efb530 Antony Chazapis
            permissions = request.backend.get_object_permissions(request.user_uniq,
588 39593b2b Giorgos Verigakis
                                            v_account, v_container, v_object)
589 61efb530 Antony Chazapis
            public = request.backend.get_object_public(request.user_uniq, v_account,
590 39593b2b Giorgos Verigakis
                                                        v_container, v_object)
591 e8886082 Antony Chazapis
        else:
592 e8886082 Antony Chazapis
            permissions = None
593 e0f916bb Antony Chazapis
            public = None
594 cca6c617 Antony Chazapis
    except NotAllowedError:
595 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
596 b956618e Antony Chazapis
    except NameError:
597 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
598 58a6c894 Antony Chazapis
    except IndexError:
599 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
600 b956618e Antony Chazapis
    
601 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
602 067cf1fc Antony Chazapis
    update_sharing_meta(request, permissions, v_account, v_container, v_object, meta)
603 e0f916bb Antony Chazapis
    update_public_meta(public, meta)
604 8cb45c13 Antony Chazapis
    
605 a8326bef Antony Chazapis
    # Evaluate conditions.
606 a8326bef Antony Chazapis
    validate_modification_preconditions(request, meta)
607 a8326bef Antony Chazapis
    try:
608 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
609 a8326bef Antony Chazapis
    except NotModified:
610 a8326bef Antony Chazapis
        response = HttpResponse(status=304)
611 4a1c29ea Antony Chazapis
        response['ETag'] = meta['ETag']
612 a8326bef Antony Chazapis
        return response
613 a8326bef Antony Chazapis
    
614 cb146cf9 Antony Chazapis
    response = HttpResponse(status=200)
615 02c0c3fa Antony Chazapis
    put_object_headers(response, meta)
616 b956618e Antony Chazapis
    return response
617 b956618e Antony Chazapis
618 22dab079 Antony Chazapis
@api_method('GET', format_allowed=True)
619 b956618e Antony Chazapis
def object_read(request, v_account, v_container, v_object):
620 b956618e Antony Chazapis
    # Normal Response Codes: 200, 206
621 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
622 b956618e Antony Chazapis
    #                       rangeNotSatisfiable (416),
623 b956618e Antony Chazapis
    #                       preconditionFailed (412),
624 b956618e Antony Chazapis
    #                       itemNotFound (404),
625 297513ba Antony Chazapis
    #                       forbidden (403),
626 b956618e Antony Chazapis
    #                       badRequest (400),
627 b956618e Antony Chazapis
    #                       notModified (304)
628 b956618e Antony Chazapis
    
629 104626e3 Antony Chazapis
    version = request.GET.get('version')
630 e8886082 Antony Chazapis
    
631 e8886082 Antony Chazapis
    # Reply with the version list. Do this first, as the object may be deleted.
632 104626e3 Antony Chazapis
    if version == 'list':
633 e8886082 Antony Chazapis
        if request.serialization == 'text':
634 e8886082 Antony Chazapis
            raise BadRequest('No format specified for version list.')
635 e8886082 Antony Chazapis
        
636 cca6c617 Antony Chazapis
        try:
637 61efb530 Antony Chazapis
            v = request.backend.list_versions(request.user_uniq, v_account,
638 39593b2b Giorgos Verigakis
                                                v_container, v_object)
639 cca6c617 Antony Chazapis
        except NotAllowedError:
640 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
641 cca6c617 Antony Chazapis
        d = {'versions': v}
642 e8886082 Antony Chazapis
        if request.serialization == 'xml':
643 e8886082 Antony Chazapis
            d['object'] = v_object
644 e8886082 Antony Chazapis
            data = render_to_string('versions.xml', d)
645 e8886082 Antony Chazapis
        elif request.serialization  == 'json':
646 c48acbfd Sofia Papagiannaki
            data = json.dumps(d, default=json_encode_decimal)
647 e8886082 Antony Chazapis
        
648 e8886082 Antony Chazapis
        response = HttpResponse(data, status=200)
649 e8886082 Antony Chazapis
        response['Content-Length'] = len(data)
650 e8886082 Antony Chazapis
        return response
651 e8886082 Antony Chazapis
    
652 b956618e Antony Chazapis
    try:
653 61efb530 Antony Chazapis
        meta = request.backend.get_object_meta(request.user_uniq, v_account,
654 39593b2b Giorgos Verigakis
                                                v_container, v_object, version)
655 e8886082 Antony Chazapis
        if version is None:
656 61efb530 Antony Chazapis
            permissions = request.backend.get_object_permissions(request.user_uniq,
657 39593b2b Giorgos Verigakis
                                            v_account, v_container, v_object)
658 61efb530 Antony Chazapis
            public = request.backend.get_object_public(request.user_uniq, v_account,
659 39593b2b Giorgos Verigakis
                                                        v_container, v_object)
660 e8886082 Antony Chazapis
        else:
661 e8886082 Antony Chazapis
            permissions = None
662 e0f916bb Antony Chazapis
            public = None
663 cca6c617 Antony Chazapis
    except NotAllowedError:
664 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
665 b956618e Antony Chazapis
    except NameError:
666 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
667 58a6c894 Antony Chazapis
    except IndexError:
668 58a6c894 Antony Chazapis
        raise ItemNotFound('Version does not exist')
669 b956618e Antony Chazapis
    
670 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
671 067cf1fc Antony Chazapis
    update_sharing_meta(request, permissions, v_account, v_container, v_object, meta)
672 e0f916bb Antony Chazapis
    update_public_meta(public, meta)
673 8cb45c13 Antony Chazapis
    
674 22dab079 Antony Chazapis
    # Evaluate conditions.
675 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
676 22dab079 Antony Chazapis
    try:
677 22dab079 Antony Chazapis
        validate_matching_preconditions(request, meta)
678 22dab079 Antony Chazapis
    except NotModified:
679 22dab079 Antony Chazapis
        response = HttpResponse(status=304)
680 4a1c29ea Antony Chazapis
        response['ETag'] = meta['ETag']
681 22dab079 Antony Chazapis
        return response
682 b956618e Antony Chazapis
    
683 8cb45c13 Antony Chazapis
    sizes = []
684 8cb45c13 Antony Chazapis
    hashmaps = []
685 8cb45c13 Antony Chazapis
    if 'X-Object-Manifest' in meta:
686 8cb45c13 Antony Chazapis
        try:
687 6d817842 Antony Chazapis
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
688 61efb530 Antony Chazapis
            objects = request.backend.list_objects(request.user_uniq, v_account,
689 39593b2b Giorgos Verigakis
                                src_container, prefix=src_name, virtual=False)
690 cca6c617 Antony Chazapis
        except NotAllowedError:
691 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
692 8cb45c13 Antony Chazapis
        except ValueError:
693 8cb45c13 Antony Chazapis
            raise BadRequest('Invalid X-Object-Manifest header')
694 8cb45c13 Antony Chazapis
        except NameError:
695 8cb45c13 Antony Chazapis
            raise ItemNotFound('Container does not exist')
696 8cb45c13 Antony Chazapis
        
697 8cb45c13 Antony Chazapis
        try:
698 8cb45c13 Antony Chazapis
            for x in objects:
699 61efb530 Antony Chazapis
                s, h = request.backend.get_object_hashmap(request.user_uniq,
700 39593b2b Giorgos Verigakis
                                        v_account, src_container, x[0], x[1])
701 8cb45c13 Antony Chazapis
                sizes.append(s)
702 8cb45c13 Antony Chazapis
                hashmaps.append(h)
703 cca6c617 Antony Chazapis
        except NotAllowedError:
704 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
705 8cb45c13 Antony Chazapis
        except NameError:
706 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
707 8cb45c13 Antony Chazapis
        except IndexError:
708 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
709 8cb45c13 Antony Chazapis
    else:
710 8cb45c13 Antony Chazapis
        try:
711 61efb530 Antony Chazapis
            s, h = request.backend.get_object_hashmap(request.user_uniq, v_account,
712 39593b2b Giorgos Verigakis
                                                v_container, v_object, version)
713 8cb45c13 Antony Chazapis
            sizes.append(s)
714 8cb45c13 Antony Chazapis
            hashmaps.append(h)
715 cca6c617 Antony Chazapis
        except NotAllowedError:
716 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
717 8cb45c13 Antony Chazapis
        except NameError:
718 8cb45c13 Antony Chazapis
            raise ItemNotFound('Object does not exist')
719 8cb45c13 Antony Chazapis
        except IndexError:
720 8cb45c13 Antony Chazapis
            raise ItemNotFound('Version does not exist')
721 b956618e Antony Chazapis
    
722 22dab079 Antony Chazapis
    # Reply with the hashmap.
723 f9f15f92 Antony Chazapis
    if 'hashmap' in request.GET and request.serialization != 'text':
724 8cb45c13 Antony Chazapis
        size = sum(sizes)
725 8cb45c13 Antony Chazapis
        hashmap = sum(hashmaps, [])
726 39593b2b Giorgos Verigakis
        d = {
727 39593b2b Giorgos Verigakis
            'block_size': request.backend.block_size,
728 39593b2b Giorgos Verigakis
            'block_hash': request.backend.hash_algorithm,
729 39593b2b Giorgos Verigakis
            'bytes': size,
730 39593b2b Giorgos Verigakis
            'hashes': hashmap}
731 22dab079 Antony Chazapis
        if request.serialization == 'xml':
732 6bc372a4 Antony Chazapis
            d['object'] = v_object
733 6bc372a4 Antony Chazapis
            data = render_to_string('hashes.xml', d)
734 22dab079 Antony Chazapis
        elif request.serialization  == 'json':
735 6bc372a4 Antony Chazapis
            data = json.dumps(d)
736 22dab079 Antony Chazapis
        
737 22dab079 Antony Chazapis
        response = HttpResponse(data, status=200)
738 02c0c3fa Antony Chazapis
        put_object_headers(response, meta)
739 22dab079 Antony Chazapis
        response['Content-Length'] = len(data)
740 22dab079 Antony Chazapis
        return response
741 22dab079 Antony Chazapis
    
742 28486f26 Antony Chazapis
    request.serialization = 'text' # Unset.
743 8cb45c13 Antony Chazapis
    return object_data_response(request, sizes, hashmaps, meta)
744 b956618e Antony Chazapis
745 76985443 Sofia Papagiannaki
@api_method('PUT', format_allowed=True)
746 b956618e Antony Chazapis
def object_write(request, v_account, v_container, v_object):
747 b956618e Antony Chazapis
    # Normal Response Codes: 201
748 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
749 b956618e Antony Chazapis
    #                       unprocessableEntity (422),
750 b956618e Antony Chazapis
    #                       lengthRequired (411),
751 3436eeb0 Antony Chazapis
    #                       conflict (409),
752 b956618e Antony Chazapis
    #                       itemNotFound (404),
753 297513ba Antony Chazapis
    #                       forbidden (403),
754 b956618e Antony Chazapis
    #                       badRequest (400)
755 7278d371 Antony Chazapis
    
756 a8326bef Antony Chazapis
    # Evaluate conditions.
757 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
758 a8326bef Antony Chazapis
        try:
759 61efb530 Antony Chazapis
            meta = request.backend.get_object_meta(request.user_uniq, v_account,
760 39593b2b Giorgos Verigakis
                                                        v_container, v_object)
761 a8326bef Antony Chazapis
        except NotAllowedError:
762 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
763 a8326bef Antony Chazapis
        except NameError:
764 a8326bef Antony Chazapis
            meta = {}
765 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
766 a8326bef Antony Chazapis
    
767 88283e9e Antony Chazapis
    copy_from = request.META.get('HTTP_X_COPY_FROM')
768 88283e9e Antony Chazapis
    move_from = request.META.get('HTTP_X_MOVE_FROM')
769 b956618e Antony Chazapis
    if copy_from or move_from:
770 ab2e317e Antony Chazapis
        content_length = get_content_length(request) # Required by the API.
771 b956618e Antony Chazapis
        
772 88283e9e Antony Chazapis
        src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
773 79bb41b7 Antony Chazapis
        if not src_account:
774 61efb530 Antony Chazapis
            src_account = request.user_uniq
775 b956618e Antony Chazapis
        if move_from:
776 83dd59c5 Antony Chazapis
            try:
777 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(move_from)
778 83dd59c5 Antony Chazapis
            except ValueError:
779 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Move-From header')
780 79bb41b7 Antony Chazapis
            version_id = copy_or_move_object(request, src_account, src_container, src_name,
781 79bb41b7 Antony Chazapis
                                                v_account, v_container, v_object, move=True)
782 b956618e Antony Chazapis
        else:
783 83dd59c5 Antony Chazapis
            try:
784 83dd59c5 Antony Chazapis
                src_container, src_name = split_container_object_string(copy_from)
785 83dd59c5 Antony Chazapis
            except ValueError:
786 83dd59c5 Antony Chazapis
                raise BadRequest('Invalid X-Copy-From header')
787 79bb41b7 Antony Chazapis
            version_id = copy_or_move_object(request, src_account, src_container, src_name,
788 79bb41b7 Antony Chazapis
                                                v_account, v_container, v_object, move=False)
789 7dd293a0 Antony Chazapis
        response = HttpResponse(status=201)
790 7dd293a0 Antony Chazapis
        response['X-Object-Version'] = version_id
791 7dd293a0 Antony Chazapis
        return response
792 b956618e Antony Chazapis
    
793 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
794 b956618e Antony Chazapis
    content_length = -1
795 b956618e Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
796 22dab079 Antony Chazapis
        content_length = get_content_length(request)
797 b956618e Antony Chazapis
    # Should be BadRequest, but API says otherwise.
798 b956618e Antony Chazapis
    if 'Content-Type' not in meta:
799 b956618e Antony Chazapis
        raise LengthRequired('Missing Content-Type header')
800 b956618e Antony Chazapis
    
801 f9f15f92 Antony Chazapis
    if 'hashmap' in request.GET:
802 f9f15f92 Antony Chazapis
        if request.serialization not in ('json', 'xml'):
803 f9f15f92 Antony Chazapis
            raise BadRequest('Invalid hashmap format')
804 f9f15f92 Antony Chazapis
        
805 76985443 Sofia Papagiannaki
        data = ''
806 39593b2b Giorgos Verigakis
        for block in socket_read_iterator(request, content_length,
807 39593b2b Giorgos Verigakis
                                            request.backend.block_size):
808 76985443 Sofia Papagiannaki
            data = '%s%s' % (data, block)
809 32a437b1 Sofia Papagiannaki
        
810 32a437b1 Sofia Papagiannaki
        if request.serialization == 'json':
811 32a437b1 Sofia Papagiannaki
            d = json.loads(data)
812 32a437b1 Sofia Papagiannaki
            if not hasattr(d, '__getitem__'):
813 32a437b1 Sofia Papagiannaki
                raise BadRequest('Invalid data formating')
814 32a437b1 Sofia Papagiannaki
            try:
815 32a437b1 Sofia Papagiannaki
                hashmap = d['hashes']
816 f899c7b4 Antony Chazapis
                size = int(d['bytes'])
817 f899c7b4 Antony Chazapis
            except:
818 32a437b1 Sofia Papagiannaki
                raise BadRequest('Invalid data formatting')
819 32a437b1 Sofia Papagiannaki
        elif request.serialization == 'xml':
820 32a437b1 Sofia Papagiannaki
            try:
821 32a437b1 Sofia Papagiannaki
                xml = minidom.parseString(data)
822 32a437b1 Sofia Papagiannaki
                obj = xml.getElementsByTagName('object')[0]
823 f899c7b4 Antony Chazapis
                size = int(obj.attributes['bytes'].value)
824 32a437b1 Sofia Papagiannaki
                
825 32a437b1 Sofia Papagiannaki
                hashes = xml.getElementsByTagName('hash')
826 32a437b1 Sofia Papagiannaki
                hashmap = []
827 32a437b1 Sofia Papagiannaki
                for hash in hashes:
828 32a437b1 Sofia Papagiannaki
                    hashmap.append(hash.firstChild.data)
829 f899c7b4 Antony Chazapis
            except:
830 32a437b1 Sofia Papagiannaki
                raise BadRequest('Invalid data formatting')
831 32a437b1 Sofia Papagiannaki
        
832 4a1c29ea Antony Chazapis
        meta.update({'ETag': hashmap_hash(request, hashmap)}) # Update ETag.
833 76985443 Sofia Papagiannaki
    else:
834 76985443 Sofia Papagiannaki
        md5 = hashlib.md5()
835 76985443 Sofia Papagiannaki
        size = 0
836 76985443 Sofia Papagiannaki
        hashmap = []
837 39593b2b Giorgos Verigakis
        for data in socket_read_iterator(request, content_length,
838 39593b2b Giorgos Verigakis
                                            request.backend.block_size):
839 76985443 Sofia Papagiannaki
            # TODO: Raise 408 (Request Timeout) if this takes too long.
840 76985443 Sofia Papagiannaki
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
841 76985443 Sofia Papagiannaki
            size += len(data)
842 39593b2b Giorgos Verigakis
            hashmap.append(request.backend.put_block(data))
843 76985443 Sofia Papagiannaki
            md5.update(data)
844 76985443 Sofia Papagiannaki
        
845 4a1c29ea Antony Chazapis
        meta['ETag'] = md5.hexdigest().lower()
846 76985443 Sofia Papagiannaki
        etag = request.META.get('HTTP_ETAG')
847 4a1c29ea Antony Chazapis
        if etag and parse_etags(etag)[0].lower() != meta['ETag']:
848 76985443 Sofia Papagiannaki
            raise UnprocessableEntity('Object ETag does not match')
849 22dab079 Antony Chazapis
    
850 22dab079 Antony Chazapis
    try:
851 61efb530 Antony Chazapis
        version_id = request.backend.update_object_hashmap(request.user_uniq,
852 39593b2b Giorgos Verigakis
                        v_account, v_container, v_object, size, hashmap, meta,
853 39593b2b Giorgos Verigakis
                        True, permissions)
854 cca6c617 Antony Chazapis
    except NotAllowedError:
855 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
856 76985443 Sofia Papagiannaki
    except IndexError, e:
857 96ee45a3 Antony Chazapis
        raise Conflict('\n'.join(e.data) + '\n')
858 22dab079 Antony Chazapis
    except NameError:
859 22dab079 Antony Chazapis
        raise ItemNotFound('Container does not exist')
860 3436eeb0 Antony Chazapis
    except ValueError:
861 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
862 1993fea9 Antony Chazapis
    except AttributeError, e:
863 96ee45a3 Antony Chazapis
        raise Conflict('\n'.join(e.data) + '\n')
864 5df6c6d1 Antony Chazapis
    except QuotaError:
865 5df6c6d1 Antony Chazapis
        raise RequestEntityTooLarge('Quota exceeded')
866 e0f916bb Antony Chazapis
    if public is not None:
867 e0f916bb Antony Chazapis
        try:
868 61efb530 Antony Chazapis
            request.backend.update_object_public(request.user_uniq, v_account,
869 39593b2b Giorgos Verigakis
                                                v_container, v_object, public)
870 e0f916bb Antony Chazapis
        except NotAllowedError:
871 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
872 e0f916bb Antony Chazapis
        except NameError:
873 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
874 b956618e Antony Chazapis
    
875 1993fea9 Antony Chazapis
    response = HttpResponse(status=201)
876 4a1c29ea Antony Chazapis
    response['ETag'] = meta['ETag']
877 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
878 b956618e Antony Chazapis
    return response
879 b956618e Antony Chazapis
880 1d5c57d3 Antony Chazapis
@api_method('POST')
881 1d5c57d3 Antony Chazapis
def object_write_form(request, v_account, v_container, v_object):
882 1d5c57d3 Antony Chazapis
    # Normal Response Codes: 201
883 1d5c57d3 Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
884 1d5c57d3 Antony Chazapis
    #                       itemNotFound (404),
885 297513ba Antony Chazapis
    #                       forbidden (403),
886 1d5c57d3 Antony Chazapis
    #                       badRequest (400)
887 1d5c57d3 Antony Chazapis
    
888 817890f2 Antony Chazapis
    request.upload_handlers = [SaveToBackendHandler(request)]
889 1d5c57d3 Antony Chazapis
    if not request.FILES.has_key('X-Object-Data'):
890 1d5c57d3 Antony Chazapis
        raise BadRequest('Missing X-Object-Data field')
891 1d5c57d3 Antony Chazapis
    file = request.FILES['X-Object-Data']
892 1d5c57d3 Antony Chazapis
    
893 1d5c57d3 Antony Chazapis
    meta = {}
894 1d5c57d3 Antony Chazapis
    meta['Content-Type'] = file.content_type
895 817890f2 Antony Chazapis
    meta['ETag'] = file.etag
896 1d5c57d3 Antony Chazapis
    
897 1d5c57d3 Antony Chazapis
    try:
898 61efb530 Antony Chazapis
        version_id = request.backend.update_object_hashmap(request.user_uniq,
899 817890f2 Antony Chazapis
                        v_account, v_container, v_object, file.size, file.hashmap, meta, True)
900 1d5c57d3 Antony Chazapis
    except NotAllowedError:
901 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
902 1d5c57d3 Antony Chazapis
    except NameError:
903 1d5c57d3 Antony Chazapis
        raise ItemNotFound('Container does not exist')
904 5df6c6d1 Antony Chazapis
    except QuotaError:
905 5df6c6d1 Antony Chazapis
        raise RequestEntityTooLarge('Quota exceeded')
906 1d5c57d3 Antony Chazapis
    
907 1d5c57d3 Antony Chazapis
    response = HttpResponse(status=201)
908 4a1c29ea Antony Chazapis
    response['ETag'] = meta['ETag']
909 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
910 1d5c57d3 Antony Chazapis
    return response
911 1d5c57d3 Antony Chazapis
912 b956618e Antony Chazapis
@api_method('COPY')
913 b956618e Antony Chazapis
def object_copy(request, v_account, v_container, v_object):
914 b956618e Antony Chazapis
    # Normal Response Codes: 201
915 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
916 b956618e Antony Chazapis
    #                       itemNotFound (404),
917 297513ba Antony Chazapis
    #                       forbidden (403),
918 b956618e Antony Chazapis
    #                       badRequest (400)
919 b956618e Antony Chazapis
    
920 88283e9e Antony Chazapis
    dest_account = request.META.get('HTTP_DESTINATION_ACCOUNT')
921 79bb41b7 Antony Chazapis
    if not dest_account:
922 61efb530 Antony Chazapis
        dest_account = request.user_uniq
923 88283e9e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
924 b956618e Antony Chazapis
    if not dest_path:
925 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
926 83dd59c5 Antony Chazapis
    try:
927 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
928 83dd59c5 Antony Chazapis
    except ValueError:
929 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
930 a8326bef Antony Chazapis
    
931 a8326bef Antony Chazapis
    # Evaluate conditions.
932 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
933 a8326bef Antony Chazapis
        src_version = request.META.get('HTTP_X_SOURCE_VERSION')
934 a8326bef Antony Chazapis
        try:
935 61efb530 Antony Chazapis
            meta = request.backend.get_object_meta(request.user_uniq, v_account,
936 39593b2b Giorgos Verigakis
                                            v_container, v_object, src_version)
937 a8326bef Antony Chazapis
        except NotAllowedError:
938 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
939 a8326bef Antony Chazapis
        except (NameError, IndexError):
940 a8326bef Antony Chazapis
            raise ItemNotFound('Container or object does not exist')
941 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
942 a8326bef Antony Chazapis
    
943 79bb41b7 Antony Chazapis
    version_id = copy_or_move_object(request, v_account, v_container, v_object,
944 79bb41b7 Antony Chazapis
                                        dest_account, dest_container, dest_name, move=False)
945 7dd293a0 Antony Chazapis
    response = HttpResponse(status=201)
946 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
947 7dd293a0 Antony Chazapis
    return response
948 b956618e Antony Chazapis
949 b956618e Antony Chazapis
@api_method('MOVE')
950 b956618e Antony Chazapis
def object_move(request, v_account, v_container, v_object):
951 b956618e Antony Chazapis
    # Normal Response Codes: 201
952 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
953 b956618e Antony Chazapis
    #                       itemNotFound (404),
954 297513ba Antony Chazapis
    #                       forbidden (403),
955 b956618e Antony Chazapis
    #                       badRequest (400)
956 b956618e Antony Chazapis
    
957 88283e9e Antony Chazapis
    dest_account = request.META.get('HTTP_DESTINATION_ACCOUNT')
958 79bb41b7 Antony Chazapis
    if not dest_account:
959 61efb530 Antony Chazapis
        dest_account = request.user_uniq
960 88283e9e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
961 b956618e Antony Chazapis
    if not dest_path:
962 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
963 83dd59c5 Antony Chazapis
    try:
964 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
965 83dd59c5 Antony Chazapis
    except ValueError:
966 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
967 a8326bef Antony Chazapis
    
968 a8326bef Antony Chazapis
    # Evaluate conditions.
969 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
970 a8326bef Antony Chazapis
        try:
971 61efb530 Antony Chazapis
            meta = request.backend.get_object_meta(request.user_uniq, v_account,
972 39593b2b Giorgos Verigakis
                                                    v_container, v_object)
973 a8326bef Antony Chazapis
        except NotAllowedError:
974 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
975 a8326bef Antony Chazapis
        except NameError:
976 a8326bef Antony Chazapis
            raise ItemNotFound('Container or object does not exist')
977 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
978 a8326bef Antony Chazapis
    
979 79bb41b7 Antony Chazapis
    version_id = copy_or_move_object(request, v_account, v_container, v_object,
980 79bb41b7 Antony Chazapis
                                        dest_account, dest_container, dest_name, move=True)
981 7dd293a0 Antony Chazapis
    response = HttpResponse(status=201)
982 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
983 7dd293a0 Antony Chazapis
    return response
984 b956618e Antony Chazapis
985 b956618e Antony Chazapis
@api_method('POST')
986 b956618e Antony Chazapis
def object_update(request, v_account, v_container, v_object):
987 e9285524 Antony Chazapis
    # Normal Response Codes: 202, 204
988 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
989 3436eeb0 Antony Chazapis
    #                       conflict (409),
990 b956618e Antony Chazapis
    #                       itemNotFound (404),
991 297513ba Antony Chazapis
    #                       forbidden (403),
992 b956618e Antony Chazapis
    #                       badRequest (400)
993 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
994 22dab079 Antony Chazapis
    content_type = meta.get('Content-Type')
995 22dab079 Antony Chazapis
    if content_type:
996 b956618e Antony Chazapis
        del(meta['Content-Type']) # Do not allow changing the Content-Type.
997 22dab079 Antony Chazapis
    
998 cfe6939d Antony Chazapis
    try:
999 61efb530 Antony Chazapis
        prev_meta = request.backend.get_object_meta(request.user_uniq, v_account,
1000 39593b2b Giorgos Verigakis
                                                    v_container, v_object)
1001 cca6c617 Antony Chazapis
    except NotAllowedError:
1002 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1003 cfe6939d Antony Chazapis
    except NameError:
1004 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
1005 a8326bef Antony Chazapis
    
1006 a8326bef Antony Chazapis
    # Evaluate conditions.
1007 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
1008 a8326bef Antony Chazapis
        validate_matching_preconditions(request, prev_meta)
1009 a8326bef Antony Chazapis
    
1010 4a1c29ea Antony Chazapis
    # If replacing, keep previous values of 'Content-Type' and 'ETag'.
1011 ac62f6da Antony Chazapis
    replace = True
1012 ac62f6da Antony Chazapis
    if 'update' in request.GET:
1013 ac62f6da Antony Chazapis
        replace = False
1014 ac62f6da Antony Chazapis
    if replace:
1015 4a1c29ea Antony Chazapis
        for k in ('Content-Type', 'ETag'):
1016 22dab079 Antony Chazapis
            if k in prev_meta:
1017 22dab079 Antony Chazapis
                meta[k] = prev_meta[k]
1018 22dab079 Antony Chazapis
    
1019 ab2e317e Antony Chazapis
    # A Content-Type or X-Source-Object header indicates data updates.
1020 ab2e317e Antony Chazapis
    src_object = request.META.get('HTTP_X_SOURCE_OBJECT')
1021 ab2e317e Antony Chazapis
    if (not content_type or content_type != 'application/octet-stream') and not src_object:
1022 77edd23d Antony Chazapis
        response = HttpResponse(status=202)
1023 77edd23d Antony Chazapis
        
1024 cca6c617 Antony Chazapis
        # Do permissions first, as it may fail easier.
1025 cca6c617 Antony Chazapis
        if permissions is not None:
1026 ac62f6da Antony Chazapis
            try:
1027 61efb530 Antony Chazapis
                request.backend.update_object_permissions(request.user_uniq,
1028 39593b2b Giorgos Verigakis
                                v_account, v_container, v_object, permissions)
1029 cca6c617 Antony Chazapis
            except NotAllowedError:
1030 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
1031 ac62f6da Antony Chazapis
            except NameError:
1032 ac62f6da Antony Chazapis
                raise ItemNotFound('Object does not exist')
1033 ac62f6da Antony Chazapis
            except ValueError:
1034 ac62f6da Antony Chazapis
                raise BadRequest('Invalid sharing header')
1035 1993fea9 Antony Chazapis
            except AttributeError, e:
1036 96ee45a3 Antony Chazapis
                raise Conflict('\n'.join(e.data) + '\n')
1037 e0f916bb Antony Chazapis
        if public is not None:
1038 e0f916bb Antony Chazapis
            try:
1039 61efb530 Antony Chazapis
                request.backend.update_object_public(request.user_uniq, v_account,
1040 39593b2b Giorgos Verigakis
                                                v_container, v_object, public)
1041 e0f916bb Antony Chazapis
            except NotAllowedError:
1042 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
1043 e0f916bb Antony Chazapis
            except NameError:
1044 e0f916bb Antony Chazapis
                raise ItemNotFound('Object does not exist')
1045 77edd23d Antony Chazapis
        if meta or replace:
1046 77edd23d Antony Chazapis
            try:
1047 61efb530 Antony Chazapis
                version_id = request.backend.update_object_meta(request.user_uniq,
1048 39593b2b Giorgos Verigakis
                                v_account, v_container, v_object, meta, replace)
1049 77edd23d Antony Chazapis
            except NotAllowedError:
1050 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
1051 77edd23d Antony Chazapis
            except NameError:
1052 77edd23d Antony Chazapis
                raise ItemNotFound('Object does not exist')        
1053 77edd23d Antony Chazapis
            response['X-Object-Version'] = version_id
1054 7dd293a0 Antony Chazapis
        
1055 7dd293a0 Antony Chazapis
        return response
1056 ac62f6da Antony Chazapis
    
1057 cfe6939d Antony Chazapis
    # Single range update. Range must be in Content-Range.
1058 22dab079 Antony Chazapis
    # Based on: http://code.google.com/p/gears/wiki/ContentRangePostProposal
1059 cfe6939d Antony Chazapis
    # (with the addition that '*' is allowed for the range - will append).
1060 22dab079 Antony Chazapis
    content_range = request.META.get('HTTP_CONTENT_RANGE')
1061 22dab079 Antony Chazapis
    if not content_range:
1062 ac62f6da Antony Chazapis
        raise BadRequest('Missing Content-Range header')
1063 22dab079 Antony Chazapis
    ranges = get_content_range(request)
1064 22dab079 Antony Chazapis
    if not ranges:
1065 ac62f6da Antony Chazapis
        raise RangeNotSatisfiable('Invalid Content-Range header')
1066 22dab079 Antony Chazapis
    
1067 cfe6939d Antony Chazapis
    try:
1068 61efb530 Antony Chazapis
        size, hashmap = request.backend.get_object_hashmap(request.user_uniq,
1069 39593b2b Giorgos Verigakis
                                            v_account, v_container, v_object)
1070 cca6c617 Antony Chazapis
    except NotAllowedError:
1071 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1072 cfe6939d Antony Chazapis
    except NameError:
1073 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
1074 cfe6939d Antony Chazapis
    
1075 cfe6939d Antony Chazapis
    offset, length, total = ranges
1076 cfe6939d Antony Chazapis
    if offset is None:
1077 cfe6939d Antony Chazapis
        offset = size
1078 cb146cf9 Antony Chazapis
    elif offset > size:
1079 cb146cf9 Antony Chazapis
        raise RangeNotSatisfiable('Supplied offset is beyond object limits')
1080 ab2e317e Antony Chazapis
    if src_object:
1081 88283e9e Antony Chazapis
        src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
1082 2cd94d81 Antony Chazapis
        if not src_account:
1083 61efb530 Antony Chazapis
            src_account = request.user_uniq
1084 ab2e317e Antony Chazapis
        src_container, src_name = split_container_object_string(src_object)
1085 ab2e317e Antony Chazapis
        src_version = request.META.get('HTTP_X_SOURCE_VERSION')
1086 ab2e317e Antony Chazapis
        try:
1087 61efb530 Antony Chazapis
            src_size, src_hashmap = request.backend.get_object_hashmap(request.user_uniq,
1088 2cd94d81 Antony Chazapis
                                        src_account, src_container, src_name, src_version)
1089 ab2e317e Antony Chazapis
        except NotAllowedError:
1090 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
1091 ab2e317e Antony Chazapis
        except NameError:
1092 ab2e317e Antony Chazapis
            raise ItemNotFound('Source object does not exist')
1093 ab2e317e Antony Chazapis
        
1094 ab2e317e Antony Chazapis
        if length is None:
1095 ab2e317e Antony Chazapis
            length = src_size
1096 ab2e317e Antony Chazapis
        elif length > src_size:
1097 ab2e317e Antony Chazapis
            raise BadRequest('Object length is smaller than range length')
1098 ab2e317e Antony Chazapis
    else:
1099 ab2e317e Antony Chazapis
        # Require either a Content-Length, or 'chunked' Transfer-Encoding.
1100 ab2e317e Antony Chazapis
        content_length = -1
1101 ab2e317e Antony Chazapis
        if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
1102 ab2e317e Antony Chazapis
            content_length = get_content_length(request)
1103 b18ef3ad Antony Chazapis
        
1104 ab2e317e Antony Chazapis
        if length is None:
1105 ab2e317e Antony Chazapis
            length = content_length
1106 ab2e317e Antony Chazapis
        else:
1107 ab2e317e Antony Chazapis
            if content_length == -1:
1108 ab2e317e Antony Chazapis
                # TODO: Get up to length bytes in chunks.
1109 ab2e317e Antony Chazapis
                length = content_length
1110 ab2e317e Antony Chazapis
            elif length != content_length:
1111 ab2e317e Antony Chazapis
                raise BadRequest('Content length does not match range length')
1112 cfe6939d Antony Chazapis
    if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)):
1113 cfe6939d Antony Chazapis
        raise RangeNotSatisfiable('Supplied range will change provided object limits')
1114 cfe6939d Antony Chazapis
    
1115 1495b972 Antony Chazapis
    dest_bytes = request.META.get('HTTP_X_OBJECT_BYTES')
1116 1495b972 Antony Chazapis
    if dest_bytes is not None:
1117 1495b972 Antony Chazapis
        dest_bytes = get_int_parameter(dest_bytes)
1118 1495b972 Antony Chazapis
        if dest_bytes is None:
1119 1495b972 Antony Chazapis
            raise BadRequest('Invalid X-Object-Bytes header')
1120 1495b972 Antony Chazapis
    
1121 ab2e317e Antony Chazapis
    if src_object:
1122 39593b2b Giorgos Verigakis
        if offset % request.backend.block_size == 0:
1123 ab2e317e Antony Chazapis
            # Update the hashes only.
1124 ab2e317e Antony Chazapis
            sbi = 0
1125 ab2e317e Antony Chazapis
            while length > 0:
1126 39593b2b Giorgos Verigakis
                bi = int(offset / request.backend.block_size)
1127 39593b2b Giorgos Verigakis
                bl = min(length, request.backend.block_size)
1128 ab2e317e Antony Chazapis
                if bi < len(hashmap):
1129 39593b2b Giorgos Verigakis
                    if bl == request.backend.block_size:
1130 ab2e317e Antony Chazapis
                        hashmap[bi] = src_hashmap[sbi]
1131 ab2e317e Antony Chazapis
                    else:
1132 39593b2b Giorgos Verigakis
                        data = request.backend.get_block(src_hashmap[sbi])
1133 39593b2b Giorgos Verigakis
                        hashmap[bi] = request.backend.update_block(hashmap[bi],
1134 39593b2b Giorgos Verigakis
                                                                data[:bl], 0)
1135 ab2e317e Antony Chazapis
                else:
1136 ab2e317e Antony Chazapis
                    hashmap.append(src_hashmap[sbi])
1137 ab2e317e Antony Chazapis
                offset += bl
1138 ab2e317e Antony Chazapis
                length -= bl
1139 ab2e317e Antony Chazapis
                sbi += 1
1140 ab2e317e Antony Chazapis
        else:
1141 ab2e317e Antony Chazapis
            data = ''
1142 ab2e317e Antony Chazapis
            sbi = 0
1143 ab2e317e Antony Chazapis
            while length > 0:
1144 39593b2b Giorgos Verigakis
                data += request.backend.get_block(src_hashmap[sbi])
1145 39593b2b Giorgos Verigakis
                if length < request.backend.block_size:
1146 ab2e317e Antony Chazapis
                    data = data[:length]
1147 39593b2b Giorgos Verigakis
                bytes = put_object_block(request, hashmap, data, offset)
1148 ab2e317e Antony Chazapis
                offset += bytes
1149 ab2e317e Antony Chazapis
                data = data[bytes:]
1150 ab2e317e Antony Chazapis
                length -= bytes
1151 ab2e317e Antony Chazapis
                sbi += 1
1152 ab2e317e Antony Chazapis
    else:
1153 ab2e317e Antony Chazapis
        data = ''
1154 39593b2b Giorgos Verigakis
        for d in socket_read_iterator(request, length,
1155 39593b2b Giorgos Verigakis
                                        request.backend.block_size):
1156 ab2e317e Antony Chazapis
            # TODO: Raise 408 (Request Timeout) if this takes too long.
1157 ab2e317e Antony Chazapis
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
1158 ab2e317e Antony Chazapis
            data += d
1159 39593b2b Giorgos Verigakis
            bytes = put_object_block(request, hashmap, data, offset)
1160 ab2e317e Antony Chazapis
            offset += bytes
1161 ab2e317e Antony Chazapis
            data = data[bytes:]
1162 ab2e317e Antony Chazapis
        if len(data) > 0:
1163 39593b2b Giorgos Verigakis
            put_object_block(request, hashmap, data, offset)
1164 cfe6939d Antony Chazapis
    
1165 cfe6939d Antony Chazapis
    if offset > size:
1166 cfe6939d Antony Chazapis
        size = offset
1167 1495b972 Antony Chazapis
    if dest_bytes is not None and dest_bytes < size:
1168 1495b972 Antony Chazapis
        size = dest_bytes
1169 39593b2b Giorgos Verigakis
        hashmap = hashmap[:(int((size - 1) / request.backend.block_size) + 1)]
1170 4a1c29ea Antony Chazapis
    meta.update({'ETag': hashmap_hash(request, hashmap)}) # Update ETag.
1171 cfe6939d Antony Chazapis
    try:
1172 61efb530 Antony Chazapis
        version_id = request.backend.update_object_hashmap(request.user_uniq,
1173 39593b2b Giorgos Verigakis
                        v_account, v_container, v_object, size, hashmap, meta,
1174 39593b2b Giorgos Verigakis
                        replace, permissions)
1175 cca6c617 Antony Chazapis
    except NotAllowedError:
1176 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1177 cfe6939d Antony Chazapis
    except NameError:
1178 cfe6939d Antony Chazapis
        raise ItemNotFound('Container does not exist')
1179 3436eeb0 Antony Chazapis
    except ValueError:
1180 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
1181 1993fea9 Antony Chazapis
    except AttributeError, e:
1182 96ee45a3 Antony Chazapis
        raise Conflict('\n'.join(e.data) + '\n')
1183 5df6c6d1 Antony Chazapis
    except QuotaError:
1184 5df6c6d1 Antony Chazapis
        raise RequestEntityTooLarge('Quota exceeded')
1185 e0f916bb Antony Chazapis
    if public is not None:
1186 e0f916bb Antony Chazapis
        try:
1187 61efb530 Antony Chazapis
            request.backend.update_object_public(request.user_uniq, v_account,
1188 39593b2b Giorgos Verigakis
                                                v_container, v_object, public)
1189 e0f916bb Antony Chazapis
        except NotAllowedError:
1190 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
1191 e0f916bb Antony Chazapis
        except NameError:
1192 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
1193 3436eeb0 Antony Chazapis
    
1194 e9285524 Antony Chazapis
    response = HttpResponse(status=204)
1195 4a1c29ea Antony Chazapis
    response['ETag'] = meta['ETag']
1196 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1197 e9285524 Antony Chazapis
    return response
1198 b956618e Antony Chazapis
1199 b956618e Antony Chazapis
@api_method('DELETE')
1200 b956618e Antony Chazapis
def object_delete(request, v_account, v_container, v_object):
1201 b956618e Antony Chazapis
    # Normal Response Codes: 204
1202 b956618e Antony Chazapis
    # Error Response Codes: serviceUnavailable (503),
1203 b956618e Antony Chazapis
    #                       itemNotFound (404),
1204 297513ba Antony Chazapis
    #                       forbidden (403),
1205 b956618e Antony Chazapis
    #                       badRequest (400)
1206 b956618e Antony Chazapis
    
1207 bbd20b55 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
1208 b956618e Antony Chazapis
    try:
1209 61efb530 Antony Chazapis
        request.backend.delete_object(request.user_uniq, v_account, v_container,
1210 39593b2b Giorgos Verigakis
                                        v_object, until)
1211 cca6c617 Antony Chazapis
    except NotAllowedError:
1212 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1213 b956618e Antony Chazapis
    except NameError:
1214 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
1215 b956618e Antony Chazapis
    return HttpResponse(status=204)
1216 b956618e Antony Chazapis
1217 b956618e Antony Chazapis
@api_method()
1218 b956618e Antony Chazapis
def method_not_allowed(request):
1219 b956618e Antony Chazapis
    raise BadRequest('Method not allowed')