Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / functions.py @ 4a669c71

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