Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / functions.py @ 78c691a8

History | View | Annotate | Download (50.1 kB)

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