Statistics
| Branch: | Tag: | Revision:

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

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 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 6b6b6c1e Antony Chazapis
from pithos.api.util import (json_encode_decimal, rename_meta_key, format_header_key, printable_header_dict,
50 6b6b6c1e Antony Chazapis
    get_account_headers, put_account_headers, get_container_headers, put_container_headers, get_object_headers,
51 6b6b6c1e Antony Chazapis
    put_object_headers, update_manifest_meta, update_sharing_meta, update_public_meta,
52 6b6b6c1e Antony Chazapis
    validate_modification_preconditions, validate_matching_preconditions, split_container_object_string,
53 6b6b6c1e Antony Chazapis
    copy_or_move_object, get_int_parameter, get_content_length, get_content_range, socket_read_iterator,
54 af7bb62f Antony Chazapis
    SaveToBackendHandler, object_data_response, put_object_block, hashmap_md5, simple_list_response, api_method)
55 5df6c6d1 Antony Chazapis
from pithos.backends.base import NotAllowedError, QuotaError
56 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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 af7bb62f Antony Chazapis
@api_method('POST', format_allowed=True)
380 b956618e Antony Chazapis
def container_update(request, v_account, v_container):
381 b956618e Antony Chazapis
    # Normal Response Codes: 202
382 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 af7bb62f Antony Chazapis
        response.content = simple_list_response(request, hashmap)
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 af7bb62f Antony Chazapis
        raise Conflict(simple_list_response(request, e.data))
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 af7bb62f Antony Chazapis
        raise Conflict(simple_list_response(request, e.data))
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 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
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 3c4bb1a0 root
    response.content = meta['ETag']
921 1d5c57d3 Antony Chazapis
    return response
922 1d5c57d3 Antony Chazapis
923 6b6b6c1e Antony Chazapis
@api_method('COPY', format_allowed=True)
924 b956618e Antony Chazapis
def object_copy(request, v_account, v_container, v_object):
925 b956618e Antony Chazapis
    # Normal Response Codes: 201
926 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
927 b956618e Antony Chazapis
    #                       itemNotFound (404),
928 297513ba Antony Chazapis
    #                       forbidden (403),
929 b956618e Antony Chazapis
    #                       badRequest (400)
930 b956618e Antony Chazapis
    
931 88283e9e Antony Chazapis
    dest_account = request.META.get('HTTP_DESTINATION_ACCOUNT')
932 79bb41b7 Antony Chazapis
    if not dest_account:
933 61efb530 Antony Chazapis
        dest_account = request.user_uniq
934 88283e9e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
935 b956618e Antony Chazapis
    if not dest_path:
936 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
937 83dd59c5 Antony Chazapis
    try:
938 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
939 83dd59c5 Antony Chazapis
    except ValueError:
940 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
941 a8326bef Antony Chazapis
    
942 a8326bef Antony Chazapis
    # Evaluate conditions.
943 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
944 a8326bef Antony Chazapis
        src_version = request.META.get('HTTP_X_SOURCE_VERSION')
945 a8326bef Antony Chazapis
        try:
946 61efb530 Antony Chazapis
            meta = request.backend.get_object_meta(request.user_uniq, v_account,
947 808cea65 Antony Chazapis
                                            v_container, v_object, 'pithos', src_version)
948 a8326bef Antony Chazapis
        except NotAllowedError:
949 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
950 a8326bef Antony Chazapis
        except (NameError, IndexError):
951 a8326bef Antony Chazapis
            raise ItemNotFound('Container or object does not exist')
952 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
953 a8326bef Antony Chazapis
    
954 79bb41b7 Antony Chazapis
    version_id = copy_or_move_object(request, v_account, v_container, v_object,
955 79bb41b7 Antony Chazapis
                                        dest_account, dest_container, dest_name, move=False)
956 7dd293a0 Antony Chazapis
    response = HttpResponse(status=201)
957 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
958 7dd293a0 Antony Chazapis
    return response
959 b956618e Antony Chazapis
960 6b6b6c1e Antony Chazapis
@api_method('MOVE', format_allowed=True)
961 b956618e Antony Chazapis
def object_move(request, v_account, v_container, v_object):
962 b956618e Antony Chazapis
    # Normal Response Codes: 201
963 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
964 b956618e Antony Chazapis
    #                       itemNotFound (404),
965 297513ba Antony Chazapis
    #                       forbidden (403),
966 b956618e Antony Chazapis
    #                       badRequest (400)
967 b956618e Antony Chazapis
    
968 88283e9e Antony Chazapis
    dest_account = request.META.get('HTTP_DESTINATION_ACCOUNT')
969 79bb41b7 Antony Chazapis
    if not dest_account:
970 61efb530 Antony Chazapis
        dest_account = request.user_uniq
971 88283e9e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
972 b956618e Antony Chazapis
    if not dest_path:
973 b956618e Antony Chazapis
        raise BadRequest('Missing Destination header')
974 83dd59c5 Antony Chazapis
    try:
975 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
976 83dd59c5 Antony Chazapis
    except ValueError:
977 83dd59c5 Antony Chazapis
        raise BadRequest('Invalid Destination header')
978 a8326bef Antony Chazapis
    
979 a8326bef Antony Chazapis
    # Evaluate conditions.
980 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
981 a8326bef Antony Chazapis
        try:
982 61efb530 Antony Chazapis
            meta = request.backend.get_object_meta(request.user_uniq, v_account,
983 808cea65 Antony Chazapis
                                                    v_container, v_object, 'pithos')
984 a8326bef Antony Chazapis
        except NotAllowedError:
985 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
986 a8326bef Antony Chazapis
        except NameError:
987 a8326bef Antony Chazapis
            raise ItemNotFound('Container or object does not exist')
988 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
989 a8326bef Antony Chazapis
    
990 79bb41b7 Antony Chazapis
    version_id = copy_or_move_object(request, v_account, v_container, v_object,
991 79bb41b7 Antony Chazapis
                                        dest_account, dest_container, dest_name, move=True)
992 7dd293a0 Antony Chazapis
    response = HttpResponse(status=201)
993 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
994 7dd293a0 Antony Chazapis
    return response
995 b956618e Antony Chazapis
996 6b6b6c1e Antony Chazapis
@api_method('POST', format_allowed=True)
997 b956618e Antony Chazapis
def object_update(request, v_account, v_container, v_object):
998 e9285524 Antony Chazapis
    # Normal Response Codes: 202, 204
999 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1000 3436eeb0 Antony Chazapis
    #                       conflict (409),
1001 b956618e Antony Chazapis
    #                       itemNotFound (404),
1002 297513ba Antony Chazapis
    #                       forbidden (403),
1003 b956618e Antony Chazapis
    #                       badRequest (400)
1004 5bc1116c Antony Chazapis
    
1005 3ab38c43 Antony Chazapis
    meta, permissions, public = get_object_headers(request)
1006 22dab079 Antony Chazapis
    content_type = meta.get('Content-Type')
1007 22dab079 Antony Chazapis
    if content_type:
1008 b956618e Antony Chazapis
        del(meta['Content-Type']) # Do not allow changing the Content-Type.
1009 22dab079 Antony Chazapis
    
1010 cfe6939d Antony Chazapis
    try:
1011 61efb530 Antony Chazapis
        prev_meta = request.backend.get_object_meta(request.user_uniq, v_account,
1012 808cea65 Antony Chazapis
                                                    v_container, v_object, 'pithos')
1013 cca6c617 Antony Chazapis
    except NotAllowedError:
1014 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1015 cfe6939d Antony Chazapis
    except NameError:
1016 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
1017 a8326bef Antony Chazapis
    
1018 a8326bef Antony Chazapis
    # Evaluate conditions.
1019 a8326bef Antony Chazapis
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
1020 a8326bef Antony Chazapis
        validate_matching_preconditions(request, prev_meta)
1021 a8326bef Antony Chazapis
    
1022 4a1c29ea Antony Chazapis
    # If replacing, keep previous values of 'Content-Type' and 'ETag'.
1023 ac62f6da Antony Chazapis
    replace = True
1024 ac62f6da Antony Chazapis
    if 'update' in request.GET:
1025 ac62f6da Antony Chazapis
        replace = False
1026 ac62f6da Antony Chazapis
    if replace:
1027 4a1c29ea Antony Chazapis
        for k in ('Content-Type', 'ETag'):
1028 22dab079 Antony Chazapis
            if k in prev_meta:
1029 22dab079 Antony Chazapis
                meta[k] = prev_meta[k]
1030 22dab079 Antony Chazapis
    
1031 ab2e317e Antony Chazapis
    # A Content-Type or X-Source-Object header indicates data updates.
1032 ab2e317e Antony Chazapis
    src_object = request.META.get('HTTP_X_SOURCE_OBJECT')
1033 ab2e317e Antony Chazapis
    if (not content_type or content_type != 'application/octet-stream') and not src_object:
1034 77edd23d Antony Chazapis
        response = HttpResponse(status=202)
1035 77edd23d Antony Chazapis
        
1036 cca6c617 Antony Chazapis
        # Do permissions first, as it may fail easier.
1037 cca6c617 Antony Chazapis
        if permissions is not None:
1038 ac62f6da Antony Chazapis
            try:
1039 61efb530 Antony Chazapis
                request.backend.update_object_permissions(request.user_uniq,
1040 39593b2b Giorgos Verigakis
                                v_account, v_container, v_object, permissions)
1041 cca6c617 Antony Chazapis
            except NotAllowedError:
1042 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
1043 ac62f6da Antony Chazapis
            except NameError:
1044 ac62f6da Antony Chazapis
                raise ItemNotFound('Object does not exist')
1045 ac62f6da Antony Chazapis
            except ValueError:
1046 ac62f6da Antony Chazapis
                raise BadRequest('Invalid sharing header')
1047 1993fea9 Antony Chazapis
            except AttributeError, e:
1048 af7bb62f Antony Chazapis
                raise Conflict(simple_list_response(request, e.data))
1049 e0f916bb Antony Chazapis
        if public is not None:
1050 e0f916bb Antony Chazapis
            try:
1051 61efb530 Antony Chazapis
                request.backend.update_object_public(request.user_uniq, v_account,
1052 39593b2b Giorgos Verigakis
                                                v_container, v_object, public)
1053 e0f916bb Antony Chazapis
            except NotAllowedError:
1054 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
1055 e0f916bb Antony Chazapis
            except NameError:
1056 e0f916bb Antony Chazapis
                raise ItemNotFound('Object does not exist')
1057 77edd23d Antony Chazapis
        if meta or replace:
1058 77edd23d Antony Chazapis
            try:
1059 61efb530 Antony Chazapis
                version_id = request.backend.update_object_meta(request.user_uniq,
1060 808cea65 Antony Chazapis
                                v_account, v_container, v_object, 'pithos', meta, replace)
1061 77edd23d Antony Chazapis
            except NotAllowedError:
1062 297513ba Antony Chazapis
                raise Forbidden('Not allowed')
1063 77edd23d Antony Chazapis
            except NameError:
1064 77edd23d Antony Chazapis
                raise ItemNotFound('Object does not exist')        
1065 77edd23d Antony Chazapis
            response['X-Object-Version'] = version_id
1066 7dd293a0 Antony Chazapis
        
1067 7dd293a0 Antony Chazapis
        return response
1068 ac62f6da Antony Chazapis
    
1069 cfe6939d Antony Chazapis
    # Single range update. Range must be in Content-Range.
1070 22dab079 Antony Chazapis
    # Based on: http://code.google.com/p/gears/wiki/ContentRangePostProposal
1071 cfe6939d Antony Chazapis
    # (with the addition that '*' is allowed for the range - will append).
1072 22dab079 Antony Chazapis
    content_range = request.META.get('HTTP_CONTENT_RANGE')
1073 22dab079 Antony Chazapis
    if not content_range:
1074 ac62f6da Antony Chazapis
        raise BadRequest('Missing Content-Range header')
1075 22dab079 Antony Chazapis
    ranges = get_content_range(request)
1076 22dab079 Antony Chazapis
    if not ranges:
1077 ac62f6da Antony Chazapis
        raise RangeNotSatisfiable('Invalid Content-Range header')
1078 22dab079 Antony Chazapis
    
1079 cfe6939d Antony Chazapis
    try:
1080 61efb530 Antony Chazapis
        size, hashmap = request.backend.get_object_hashmap(request.user_uniq,
1081 39593b2b Giorgos Verigakis
                                            v_account, v_container, v_object)
1082 cca6c617 Antony Chazapis
    except NotAllowedError:
1083 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1084 cfe6939d Antony Chazapis
    except NameError:
1085 cfe6939d Antony Chazapis
        raise ItemNotFound('Object does not exist')
1086 cfe6939d Antony Chazapis
    
1087 cfe6939d Antony Chazapis
    offset, length, total = ranges
1088 cfe6939d Antony Chazapis
    if offset is None:
1089 cfe6939d Antony Chazapis
        offset = size
1090 cb146cf9 Antony Chazapis
    elif offset > size:
1091 cb146cf9 Antony Chazapis
        raise RangeNotSatisfiable('Supplied offset is beyond object limits')
1092 ab2e317e Antony Chazapis
    if src_object:
1093 88283e9e Antony Chazapis
        src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
1094 2cd94d81 Antony Chazapis
        if not src_account:
1095 61efb530 Antony Chazapis
            src_account = request.user_uniq
1096 ab2e317e Antony Chazapis
        src_container, src_name = split_container_object_string(src_object)
1097 ab2e317e Antony Chazapis
        src_version = request.META.get('HTTP_X_SOURCE_VERSION')
1098 ab2e317e Antony Chazapis
        try:
1099 61efb530 Antony Chazapis
            src_size, src_hashmap = request.backend.get_object_hashmap(request.user_uniq,
1100 2cd94d81 Antony Chazapis
                                        src_account, src_container, src_name, src_version)
1101 ab2e317e Antony Chazapis
        except NotAllowedError:
1102 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
1103 ab2e317e Antony Chazapis
        except NameError:
1104 ab2e317e Antony Chazapis
            raise ItemNotFound('Source object does not exist')
1105 ab2e317e Antony Chazapis
        
1106 ab2e317e Antony Chazapis
        if length is None:
1107 ab2e317e Antony Chazapis
            length = src_size
1108 ab2e317e Antony Chazapis
        elif length > src_size:
1109 ab2e317e Antony Chazapis
            raise BadRequest('Object length is smaller than range length')
1110 ab2e317e Antony Chazapis
    else:
1111 ab2e317e Antony Chazapis
        # Require either a Content-Length, or 'chunked' Transfer-Encoding.
1112 ab2e317e Antony Chazapis
        content_length = -1
1113 ab2e317e Antony Chazapis
        if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
1114 ab2e317e Antony Chazapis
            content_length = get_content_length(request)
1115 b18ef3ad Antony Chazapis
        
1116 ab2e317e Antony Chazapis
        if length is None:
1117 ab2e317e Antony Chazapis
            length = content_length
1118 ab2e317e Antony Chazapis
        else:
1119 ab2e317e Antony Chazapis
            if content_length == -1:
1120 ab2e317e Antony Chazapis
                # TODO: Get up to length bytes in chunks.
1121 ab2e317e Antony Chazapis
                length = content_length
1122 ab2e317e Antony Chazapis
            elif length != content_length:
1123 ab2e317e Antony Chazapis
                raise BadRequest('Content length does not match range length')
1124 cfe6939d Antony Chazapis
    if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)):
1125 cfe6939d Antony Chazapis
        raise RangeNotSatisfiable('Supplied range will change provided object limits')
1126 cfe6939d Antony Chazapis
    
1127 1495b972 Antony Chazapis
    dest_bytes = request.META.get('HTTP_X_OBJECT_BYTES')
1128 1495b972 Antony Chazapis
    if dest_bytes is not None:
1129 1495b972 Antony Chazapis
        dest_bytes = get_int_parameter(dest_bytes)
1130 1495b972 Antony Chazapis
        if dest_bytes is None:
1131 1495b972 Antony Chazapis
            raise BadRequest('Invalid X-Object-Bytes header')
1132 1495b972 Antony Chazapis
    
1133 ab2e317e Antony Chazapis
    if src_object:
1134 39593b2b Giorgos Verigakis
        if offset % request.backend.block_size == 0:
1135 ab2e317e Antony Chazapis
            # Update the hashes only.
1136 ab2e317e Antony Chazapis
            sbi = 0
1137 ab2e317e Antony Chazapis
            while length > 0:
1138 39593b2b Giorgos Verigakis
                bi = int(offset / request.backend.block_size)
1139 39593b2b Giorgos Verigakis
                bl = min(length, request.backend.block_size)
1140 ab2e317e Antony Chazapis
                if bi < len(hashmap):
1141 39593b2b Giorgos Verigakis
                    if bl == request.backend.block_size:
1142 ab2e317e Antony Chazapis
                        hashmap[bi] = src_hashmap[sbi]
1143 ab2e317e Antony Chazapis
                    else:
1144 39593b2b Giorgos Verigakis
                        data = request.backend.get_block(src_hashmap[sbi])
1145 39593b2b Giorgos Verigakis
                        hashmap[bi] = request.backend.update_block(hashmap[bi],
1146 39593b2b Giorgos Verigakis
                                                                data[:bl], 0)
1147 ab2e317e Antony Chazapis
                else:
1148 ab2e317e Antony Chazapis
                    hashmap.append(src_hashmap[sbi])
1149 ab2e317e Antony Chazapis
                offset += bl
1150 ab2e317e Antony Chazapis
                length -= bl
1151 ab2e317e Antony Chazapis
                sbi += 1
1152 ab2e317e Antony Chazapis
        else:
1153 ab2e317e Antony Chazapis
            data = ''
1154 ab2e317e Antony Chazapis
            sbi = 0
1155 ab2e317e Antony Chazapis
            while length > 0:
1156 39593b2b Giorgos Verigakis
                data += request.backend.get_block(src_hashmap[sbi])
1157 39593b2b Giorgos Verigakis
                if length < request.backend.block_size:
1158 ab2e317e Antony Chazapis
                    data = data[:length]
1159 39593b2b Giorgos Verigakis
                bytes = put_object_block(request, hashmap, data, offset)
1160 ab2e317e Antony Chazapis
                offset += bytes
1161 ab2e317e Antony Chazapis
                data = data[bytes:]
1162 ab2e317e Antony Chazapis
                length -= bytes
1163 ab2e317e Antony Chazapis
                sbi += 1
1164 ab2e317e Antony Chazapis
    else:
1165 ab2e317e Antony Chazapis
        data = ''
1166 39593b2b Giorgos Verigakis
        for d in socket_read_iterator(request, length,
1167 39593b2b Giorgos Verigakis
                                        request.backend.block_size):
1168 ab2e317e Antony Chazapis
            # TODO: Raise 408 (Request Timeout) if this takes too long.
1169 ab2e317e Antony Chazapis
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
1170 ab2e317e Antony Chazapis
            data += d
1171 39593b2b Giorgos Verigakis
            bytes = put_object_block(request, hashmap, data, offset)
1172 ab2e317e Antony Chazapis
            offset += bytes
1173 ab2e317e Antony Chazapis
            data = data[bytes:]
1174 ab2e317e Antony Chazapis
        if len(data) > 0:
1175 39593b2b Giorgos Verigakis
            put_object_block(request, hashmap, data, offset)
1176 cfe6939d Antony Chazapis
    
1177 cfe6939d Antony Chazapis
    if offset > size:
1178 cfe6939d Antony Chazapis
        size = offset
1179 1495b972 Antony Chazapis
    if dest_bytes is not None and dest_bytes < size:
1180 1495b972 Antony Chazapis
        size = dest_bytes
1181 39593b2b Giorgos Verigakis
        hashmap = hashmap[:(int((size - 1) / request.backend.block_size) + 1)]
1182 cddcf432 chazapis
    meta.update({'ETag': hashmap_md5(request, hashmap, size)}) # Update ETag.
1183 cfe6939d Antony Chazapis
    try:
1184 61efb530 Antony Chazapis
        version_id = request.backend.update_object_hashmap(request.user_uniq,
1185 808cea65 Antony Chazapis
                        v_account, v_container, v_object, size, hashmap,
1186 808cea65 Antony Chazapis
                        'pithos', meta, replace, permissions)
1187 cca6c617 Antony Chazapis
    except NotAllowedError:
1188 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1189 cfe6939d Antony Chazapis
    except NameError:
1190 cfe6939d Antony Chazapis
        raise ItemNotFound('Container does not exist')
1191 3436eeb0 Antony Chazapis
    except ValueError:
1192 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
1193 1993fea9 Antony Chazapis
    except AttributeError, e:
1194 af7bb62f Antony Chazapis
        raise Conflict(simple_list_response(request, e.data))
1195 5df6c6d1 Antony Chazapis
    except QuotaError:
1196 5df6c6d1 Antony Chazapis
        raise RequestEntityTooLarge('Quota exceeded')
1197 e0f916bb Antony Chazapis
    if public is not None:
1198 e0f916bb Antony Chazapis
        try:
1199 61efb530 Antony Chazapis
            request.backend.update_object_public(request.user_uniq, v_account,
1200 39593b2b Giorgos Verigakis
                                                v_container, v_object, public)
1201 e0f916bb Antony Chazapis
        except NotAllowedError:
1202 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
1203 e0f916bb Antony Chazapis
        except NameError:
1204 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
1205 3436eeb0 Antony Chazapis
    
1206 e9285524 Antony Chazapis
    response = HttpResponse(status=204)
1207 4a1c29ea Antony Chazapis
    response['ETag'] = meta['ETag']
1208 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1209 e9285524 Antony Chazapis
    return response
1210 b956618e Antony Chazapis
1211 b956618e Antony Chazapis
@api_method('DELETE')
1212 b956618e Antony Chazapis
def object_delete(request, v_account, v_container, v_object):
1213 b956618e Antony Chazapis
    # Normal Response Codes: 204
1214 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1215 b956618e Antony Chazapis
    #                       itemNotFound (404),
1216 297513ba Antony Chazapis
    #                       forbidden (403),
1217 b956618e Antony Chazapis
    #                       badRequest (400)
1218 b956618e Antony Chazapis
    
1219 bbd20b55 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
1220 b956618e Antony Chazapis
    try:
1221 61efb530 Antony Chazapis
        request.backend.delete_object(request.user_uniq, v_account, v_container,
1222 39593b2b Giorgos Verigakis
                                        v_object, until)
1223 cca6c617 Antony Chazapis
    except NotAllowedError:
1224 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
1225 b956618e Antony Chazapis
    except NameError:
1226 b956618e Antony Chazapis
        raise ItemNotFound('Object does not exist')
1227 b956618e Antony Chazapis
    return HttpResponse(status=204)
1228 b956618e Antony Chazapis
1229 b956618e Antony Chazapis
@api_method()
1230 b956618e Antony Chazapis
def method_not_allowed(request):
1231 b956618e Antony Chazapis
    raise BadRequest('Method not allowed')