Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / functions.py @ daf4fb05

History | View | Annotate | Download (50 kB)

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