Statistics
| Branch: | Tag: | Revision:

root / pithos / api / functions.py @ a7dff008

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