Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / functions.py @ 2560c061

History | View | Annotate | Download (59.9 kB)

1 c598a8a7 Sofia Papagiannaki
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
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 2715ade4 Sofia Papagiannaki
#
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 2715ade4 Sofia Papagiannaki
#
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 2715ade4 Sofia Papagiannaki
#
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 2715ade4 Sofia Papagiannaki
#
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 b956618e Antony Chazapis
from django.http import HttpResponse
37 b956618e Antony Chazapis
from django.template.loader import render_to_string
38 b956618e Antony Chazapis
from django.utils import simplejson as json
39 b956618e Antony Chazapis
from django.utils.http import parse_etags
40 88283e9e Antony Chazapis
from django.utils.encoding import smart_str
41 8fbfd37b Antony Chazapis
from django.views.decorators.csrf import csrf_exempt
42 b956618e Antony Chazapis
43 e6fb591c Ilias Tsitsimpis
from astakosclient import AstakosClient
44 1713c946 Antony Chazapis
45 65bbcd43 Christos Stavrakakis
from snf_django.lib import api
46 bd40abfa Christos Stavrakakis
from snf_django.lib.api import faults
47 bd40abfa Christos Stavrakakis
48 2715ade4 Sofia Papagiannaki
from pithos.api.util import (
49 6255b97d Sofia Papagiannaki
    json_encode_decimal, rename_meta_key, format_header_key,
50 6255b97d Sofia Papagiannaki
    printable_header_dict, get_account_headers, put_account_headers,
51 6255b97d Sofia Papagiannaki
    get_container_headers, put_container_headers, get_object_headers,
52 6255b97d Sofia Papagiannaki
    put_object_headers, update_manifest_meta, update_sharing_meta,
53 6255b97d Sofia Papagiannaki
    update_public_meta, validate_modification_preconditions,
54 6255b97d Sofia Papagiannaki
    validate_matching_preconditions, split_container_object_string,
55 6255b97d Sofia Papagiannaki
    copy_or_move_object, get_int_parameter, get_content_length,
56 6255b97d Sofia Papagiannaki
    get_content_range, socket_read_iterator, SaveToBackendHandler,
57 6255b97d Sofia Papagiannaki
    object_data_response, put_object_block, hashmap_md5, simple_list_response,
58 133e3fcf Sofia Papagiannaki
    api_method, is_uuid, retrieve_uuid, retrieve_uuids,
59 3b8f938b Sofia Papagiannaki
    retrieve_displaynames, Checksum, NoChecksum
60 27932481 Sofia Papagiannaki
)
61 890c2065 Sofia Papagiannaki
62 469d0997 Georgios D. Tsoukalas
from pithos.api.settings import (UPDATE_MD5, TRANSLATE_UUIDS,
63 f759041f Ilias Tsitsimpis
                                 SERVICE_TOKEN, ASTAKOS_AUTH_URL)
64 b956618e Antony Chazapis
65 3bdb9222 Sofia Papagiannaki
from pithos.api import settings
66 3bdb9222 Sofia Papagiannaki
67 6255b97d Sofia Papagiannaki
from pithos.backends.base import (
68 6255b97d Sofia Papagiannaki
    NotAllowedError, QuotaError, ContainerNotEmpty, ItemNotExists,
69 3a5994a8 Sofia Papagiannaki
    VersionNotExists, ContainerExists, InvalidHash)
70 7efc9f86 Sofia Papagiannaki
71 6e147ecc Antony Chazapis
from pithos.backends.filter import parse_filters
72 6e147ecc Antony Chazapis
73 65bbcd43 Christos Stavrakakis
import logging
74 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
75 b956618e Antony Chazapis
76 bd40abfa Christos Stavrakakis
77 469d0997 Georgios D. Tsoukalas
def get_uuids(names):
78 469d0997 Georgios D. Tsoukalas
    try:
79 f759041f Ilias Tsitsimpis
        astakos = AstakosClient(SERVICE_TOKEN, ASTAKOS_AUTH_URL,
80 f759041f Ilias Tsitsimpis
                                retry=2, use_pool=True, logger=logger)
81 f759041f Ilias Tsitsimpis
        uuids = astakos.service_get_uuids(names)
82 469d0997 Georgios D. Tsoukalas
    except Exception, e:
83 469d0997 Georgios D. Tsoukalas
        logger.exception(e)
84 469d0997 Georgios D. Tsoukalas
        return {}
85 469d0997 Georgios D. Tsoukalas
86 469d0997 Georgios D. Tsoukalas
    return uuids
87 469d0997 Georgios D. Tsoukalas
88 b956618e Antony Chazapis
89 8fbfd37b Antony Chazapis
@csrf_exempt
90 b956618e Antony Chazapis
def top_demux(request):
91 b956618e Antony Chazapis
    if request.method == 'GET':
92 2715ade4 Sofia Papagiannaki
        try:
93 2715ade4 Sofia Papagiannaki
            request.GET['X-Auth-Token']
94 2715ade4 Sofia Papagiannaki
        except KeyError:
95 2715ade4 Sofia Papagiannaki
            try:
96 2715ade4 Sofia Papagiannaki
                request.META['HTTP_X_AUTH_TOKEN']
97 2715ade4 Sofia Papagiannaki
            except KeyError:
98 2715ade4 Sofia Papagiannaki
                return authenticate(request)
99 2715ade4 Sofia Papagiannaki
        return account_list(request)
100 b956618e Antony Chazapis
    else:
101 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request, allowed_methods=['GET'])
102 b956618e Antony Chazapis
103 2715ade4 Sofia Papagiannaki
104 8fbfd37b Antony Chazapis
@csrf_exempt
105 b956618e Antony Chazapis
def account_demux(request, v_account):
106 469d0997 Georgios D. Tsoukalas
    if TRANSLATE_UUIDS:
107 469d0997 Georgios D. Tsoukalas
        if not is_uuid(v_account):
108 469d0997 Georgios D. Tsoukalas
            uuids = get_uuids([v_account])
109 469d0997 Georgios D. Tsoukalas
            if not uuids or not v_account in uuids:
110 469d0997 Georgios D. Tsoukalas
                return HttpResponse(status=404)
111 469d0997 Georgios D. Tsoukalas
            v_account = uuids[v_account]
112 469d0997 Georgios D. Tsoukalas
113 b956618e Antony Chazapis
    if request.method == 'HEAD':
114 b956618e Antony Chazapis
        return account_meta(request, v_account)
115 b956618e Antony Chazapis
    elif request.method == 'POST':
116 b956618e Antony Chazapis
        return account_update(request, v_account)
117 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
118 83dd59c5 Antony Chazapis
        return container_list(request, v_account)
119 b956618e Antony Chazapis
    else:
120 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
121 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['HEAD',
122 2aba7764 Sofia Papagiannaki
                                                           'POST',
123 2aba7764 Sofia Papagiannaki
                                                           'GET'])
124 b956618e Antony Chazapis
125 2715ade4 Sofia Papagiannaki
126 8fbfd37b Antony Chazapis
@csrf_exempt
127 b956618e Antony Chazapis
def container_demux(request, v_account, v_container):
128 469d0997 Georgios D. Tsoukalas
    if TRANSLATE_UUIDS:
129 469d0997 Georgios D. Tsoukalas
        if not is_uuid(v_account):
130 469d0997 Georgios D. Tsoukalas
            uuids = get_uuids([v_account])
131 469d0997 Georgios D. Tsoukalas
            if not uuids or not v_account in uuids:
132 469d0997 Georgios D. Tsoukalas
                return HttpResponse(status=404)
133 469d0997 Georgios D. Tsoukalas
            v_account = uuids[v_account]
134 469d0997 Georgios D. Tsoukalas
135 b956618e Antony Chazapis
    if request.method == 'HEAD':
136 b956618e Antony Chazapis
        return container_meta(request, v_account, v_container)
137 b956618e Antony Chazapis
    elif request.method == 'PUT':
138 b956618e Antony Chazapis
        return container_create(request, v_account, v_container)
139 b956618e Antony Chazapis
    elif request.method == 'POST':
140 b956618e Antony Chazapis
        return container_update(request, v_account, v_container)
141 b956618e Antony Chazapis
    elif request.method == 'DELETE':
142 b956618e Antony Chazapis
        return container_delete(request, v_account, v_container)
143 83dd59c5 Antony Chazapis
    elif request.method == 'GET':
144 83dd59c5 Antony Chazapis
        return object_list(request, v_account, v_container)
145 b956618e Antony Chazapis
    else:
146 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
147 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['HEAD',
148 2aba7764 Sofia Papagiannaki
                                                           'PUT',
149 2aba7764 Sofia Papagiannaki
                                                           'POST',
150 2aba7764 Sofia Papagiannaki
                                                           'DELETE',
151 2aba7764 Sofia Papagiannaki
                                                           'GET'])
152 b956618e Antony Chazapis
153 2715ade4 Sofia Papagiannaki
154 8fbfd37b Antony Chazapis
@csrf_exempt
155 b956618e Antony Chazapis
def object_demux(request, v_account, v_container, v_object):
156 67100dd2 Ilias Tsitsimpis
    # Helper to avoid placing the token in the URL
157 67100dd2 Ilias Tsitsimpis
    # when loading objects from a browser.
158 469d0997 Georgios D. Tsoukalas
    if TRANSLATE_UUIDS:
159 469d0997 Georgios D. Tsoukalas
        if not is_uuid(v_account):
160 469d0997 Georgios D. Tsoukalas
            uuids = get_uuids([v_account])
161 469d0997 Georgios D. Tsoukalas
            if not uuids or not v_account in uuids:
162 469d0997 Georgios D. Tsoukalas
                return HttpResponse(status=404)
163 469d0997 Georgios D. Tsoukalas
            v_account = uuids[v_account]
164 469d0997 Georgios D. Tsoukalas
165 b956618e Antony Chazapis
    if request.method == 'HEAD':
166 b956618e Antony Chazapis
        return object_meta(request, v_account, v_container, v_object)
167 b956618e Antony Chazapis
    elif request.method == 'GET':
168 b956618e Antony Chazapis
        return object_read(request, v_account, v_container, v_object)
169 b956618e Antony Chazapis
    elif request.method == 'PUT':
170 b956618e Antony Chazapis
        return object_write(request, v_account, v_container, v_object)
171 b956618e Antony Chazapis
    elif request.method == 'COPY':
172 b956618e Antony Chazapis
        return object_copy(request, v_account, v_container, v_object)
173 b956618e Antony Chazapis
    elif request.method == 'MOVE':
174 b956618e Antony Chazapis
        return object_move(request, v_account, v_container, v_object)
175 b956618e Antony Chazapis
    elif request.method == 'POST':
176 67100dd2 Ilias Tsitsimpis
        if request.META.get(
177 67100dd2 Ilias Tsitsimpis
                'CONTENT_TYPE', '').startswith('multipart/form-data'):
178 1d5c57d3 Antony Chazapis
            return object_write_form(request, v_account, v_container, v_object)
179 b956618e Antony Chazapis
        return object_update(request, v_account, v_container, v_object)
180 b956618e Antony Chazapis
    elif request.method == 'DELETE':
181 b956618e Antony Chazapis
        return object_delete(request, v_account, v_container, v_object)
182 b956618e Antony Chazapis
    else:
183 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request, allowed_methods=['HEAD',
184 2aba7764 Sofia Papagiannaki
                                                                    'GET',
185 2aba7764 Sofia Papagiannaki
                                                                    'PUT',
186 2aba7764 Sofia Papagiannaki
                                                                    'COPY',
187 2aba7764 Sofia Papagiannaki
                                                                    'MOVE',
188 2aba7764 Sofia Papagiannaki
                                                                    'POST',
189 2aba7764 Sofia Papagiannaki
                                                                    'DELETE'])
190 b956618e Antony Chazapis
191 2715ade4 Sofia Papagiannaki
192 e805709b Sofia Papagiannaki
@api_method('GET', token_required=False, user_required=False, logger=logger)
193 b956618e Antony Chazapis
def authenticate(request):
194 b956618e Antony Chazapis
    # Normal Response Codes: 204
195 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
196 297513ba Antony Chazapis
    #                       forbidden (403),
197 b956618e Antony Chazapis
    #                       badRequest (400)
198 2715ade4 Sofia Papagiannaki
199 b956618e Antony Chazapis
    x_auth_user = request.META.get('HTTP_X_AUTH_USER')
200 b956618e Antony Chazapis
    x_auth_key = request.META.get('HTTP_X_AUTH_KEY')
201 b956618e Antony Chazapis
    if not x_auth_user or not x_auth_key:
202 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Missing X-Auth-User or X-Auth-Key header')
203 b956618e Antony Chazapis
    response = HttpResponse(status=204)
204 2715ade4 Sofia Papagiannaki
205 f7667baf Antony Chazapis
    uri = request.build_absolute_uri()
206 f7667baf Antony Chazapis
    if '?' in uri:
207 f7667baf Antony Chazapis
        uri = uri[:uri.find('?')]
208 2715ade4 Sofia Papagiannaki
209 36d368f7 Antony Chazapis
    response['X-Auth-Token'] = x_auth_key
210 2715ade4 Sofia Papagiannaki
    response['X-Storage-Url'] = uri + ('' if uri.endswith('/')
211 2715ade4 Sofia Papagiannaki
                                       else '/') + x_auth_user
212 b956618e Antony Chazapis
    return response
213 b956618e Antony Chazapis
214 2715ade4 Sofia Papagiannaki
215 6e0f3e65 Sofia Papagiannaki
@api_method('GET', format_allowed=True, user_required=True, logger=logger,
216 6e0f3e65 Sofia Papagiannaki
            serializations=["text", "xml", "json"])
217 f6c97079 Antony Chazapis
def account_list(request):
218 f6c97079 Antony Chazapis
    # Normal Response Codes: 200, 204
219 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
220 f6c97079 Antony Chazapis
    #                       badRequest (400)
221 f6c97079 Antony Chazapis
    response = HttpResponse()
222 2715ade4 Sofia Papagiannaki
223 f6c97079 Antony Chazapis
    marker = request.GET.get('marker')
224 f6c97079 Antony Chazapis
    limit = get_int_parameter(request.GET.get('limit'))
225 f6c97079 Antony Chazapis
    if not limit:
226 3bdb9222 Sofia Papagiannaki
        limit = settings.API_LIST_LIMIT
227 2715ade4 Sofia Papagiannaki
228 61efb530 Antony Chazapis
    accounts = request.backend.list_accounts(request.user_uniq, marker, limit)
229 2715ade4 Sofia Papagiannaki
230 f6c97079 Antony Chazapis
    if request.serialization == 'text':
231 469d0997 Georgios D. Tsoukalas
        if TRANSLATE_UUIDS:
232 469d0997 Georgios D. Tsoukalas
            accounts = retrieve_displaynames(
233 67100dd2 Ilias Tsitsimpis
                getattr(request, 'token', None), accounts)
234 f6c97079 Antony Chazapis
        if len(accounts) == 0:
235 f6c97079 Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
236 f6c97079 Antony Chazapis
            response.status_code = 204
237 f6c97079 Antony Chazapis
            return response
238 f6c97079 Antony Chazapis
        response.status_code = 200
239 f6c97079 Antony Chazapis
        response.content = '\n'.join(accounts) + '\n'
240 f6c97079 Antony Chazapis
        return response
241 2715ade4 Sofia Papagiannaki
242 f6c97079 Antony Chazapis
    account_meta = []
243 f6c97079 Antony Chazapis
    for x in accounts:
244 690747fe Antony Chazapis
        if x == request.user_uniq:
245 690747fe Antony Chazapis
            continue
246 f6c97079 Antony Chazapis
        try:
247 2715ade4 Sofia Papagiannaki
            meta = request.backend.get_account_meta(
248 3b8f938b Sofia Papagiannaki
                request.user_uniq, x, 'pithos', include_user_defined=False)
249 61efb530 Antony Chazapis
            groups = request.backend.get_account_groups(request.user_uniq, x)
250 f6c97079 Antony Chazapis
        except NotAllowedError:
251 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
252 f6c97079 Antony Chazapis
        else:
253 804e8fe7 Antony Chazapis
            rename_meta_key(meta, 'modified', 'last_modified')
254 2715ade4 Sofia Papagiannaki
            rename_meta_key(
255 2715ade4 Sofia Papagiannaki
                meta, 'until_timestamp', 'x_account_until_timestamp')
256 690747fe Antony Chazapis
            if groups:
257 2715ade4 Sofia Papagiannaki
                meta['X-Account-Group'] = printable_header_dict(
258 2715ade4 Sofia Papagiannaki
                    dict([(k, ','.join(v)) for k, v in groups.iteritems()]))
259 f6c97079 Antony Chazapis
            account_meta.append(printable_header_dict(meta))
260 469d0997 Georgios D. Tsoukalas
261 469d0997 Georgios D. Tsoukalas
    if TRANSLATE_UUIDS:
262 469d0997 Georgios D. Tsoukalas
        uuids = list(d['name'] for d in account_meta)
263 469d0997 Georgios D. Tsoukalas
        catalog = retrieve_displaynames(
264 67100dd2 Ilias Tsitsimpis
            getattr(request, 'token', None), uuids, return_dict=True)
265 469d0997 Georgios D. Tsoukalas
        for meta in account_meta:
266 469d0997 Georgios D. Tsoukalas
            meta['name'] = catalog.get(meta.get('name'))
267 469d0997 Georgios D. Tsoukalas
268 f6c97079 Antony Chazapis
    if request.serialization == 'xml':
269 f6c97079 Antony Chazapis
        data = render_to_string('accounts.xml', {'accounts': account_meta})
270 2715ade4 Sofia Papagiannaki
    elif request.serialization == 'json':
271 f6c97079 Antony Chazapis
        data = json.dumps(account_meta)
272 f6c97079 Antony Chazapis
    response.status_code = 200
273 f6c97079 Antony Chazapis
    response.content = data
274 f6c97079 Antony Chazapis
    return response
275 f6c97079 Antony Chazapis
276 2715ade4 Sofia Papagiannaki
277 65bbcd43 Christos Stavrakakis
@api_method('HEAD', user_required=True, logger=logger)
278 b956618e Antony Chazapis
def account_meta(request, v_account):
279 b956618e Antony Chazapis
    # Normal Response Codes: 204
280 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
281 297513ba Antony Chazapis
    #                       forbidden (403),
282 b956618e Antony Chazapis
    #                       badRequest (400)
283 2715ade4 Sofia Papagiannaki
284 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
285 cca6c617 Antony Chazapis
    try:
286 2715ade4 Sofia Papagiannaki
        meta = request.backend.get_account_meta(
287 3b8f938b Sofia Papagiannaki
            request.user_uniq, v_account, 'pithos', until)
288 2715ade4 Sofia Papagiannaki
        groups = request.backend.get_account_groups(
289 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account)
290 469d0997 Georgios D. Tsoukalas
291 469d0997 Georgios D. Tsoukalas
        if TRANSLATE_UUIDS:
292 469d0997 Georgios D. Tsoukalas
            for k in groups:
293 469d0997 Georgios D. Tsoukalas
                groups[k] = retrieve_displaynames(
294 67100dd2 Ilias Tsitsimpis
                    getattr(request, 'token', None), groups[k])
295 2715ade4 Sofia Papagiannaki
        policy = request.backend.get_account_policy(
296 3b8f938b Sofia Papagiannaki
            request.user_uniq, v_account)
297 78e1f8da Sofia Papagiannaki
        logger.debug(policy)
298 cca6c617 Antony Chazapis
    except NotAllowedError:
299 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
300 2715ade4 Sofia Papagiannaki
301 a8326bef Antony Chazapis
    validate_modification_preconditions(request, meta)
302 2715ade4 Sofia Papagiannaki
303 b956618e Antony Chazapis
    response = HttpResponse(status=204)
304 647a5f48 Antony Chazapis
    put_account_headers(response, meta, groups, policy)
305 b956618e Antony Chazapis
    return response
306 b956618e Antony Chazapis
307 2715ade4 Sofia Papagiannaki
308 65bbcd43 Christos Stavrakakis
@api_method('POST', user_required=True, logger=logger)
309 b956618e Antony Chazapis
def account_update(request, v_account):
310 b956618e Antony Chazapis
    # Normal Response Codes: 202
311 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
312 297513ba Antony Chazapis
    #                       forbidden (403),
313 b956618e Antony Chazapis
    #                       badRequest (400)
314 2715ade4 Sofia Papagiannaki
315 02c0c3fa Antony Chazapis
    meta, groups = get_account_headers(request)
316 469d0997 Georgios D. Tsoukalas
    for k in groups:
317 469d0997 Georgios D. Tsoukalas
        if TRANSLATE_UUIDS:
318 469d0997 Georgios D. Tsoukalas
            try:
319 469d0997 Georgios D. Tsoukalas
                groups[k] = retrieve_uuids(
320 67100dd2 Ilias Tsitsimpis
                    getattr(request, 'token', None),
321 67100dd2 Ilias Tsitsimpis
                    groups[k],
322 67100dd2 Ilias Tsitsimpis
                    fail_silently=False)
323 469d0997 Georgios D. Tsoukalas
            except ItemNotExists, e:
324 bd40abfa Christos Stavrakakis
                raise faults.BadRequest(
325 67100dd2 Ilias Tsitsimpis
                    'Bad X-Account-Group header value: %s' % e)
326 469d0997 Georgios D. Tsoukalas
        else:
327 469d0997 Georgios D. Tsoukalas
            try:
328 469d0997 Georgios D. Tsoukalas
                retrieve_displaynames(
329 469d0997 Georgios D. Tsoukalas
                    getattr(request, 'token', None),
330 469d0997 Georgios D. Tsoukalas
                    groups[k],
331 469d0997 Georgios D. Tsoukalas
                    fail_silently=False)
332 469d0997 Georgios D. Tsoukalas
            except ItemNotExists, e:
333 bd40abfa Christos Stavrakakis
                raise faults.BadRequest(
334 67100dd2 Ilias Tsitsimpis
                    'Bad X-Account-Group header value: %s' % e)
335 a6eb13e9 Antony Chazapis
    replace = True
336 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
337 b18ef3ad Antony Chazapis
        replace = False
338 02c0c3fa Antony Chazapis
    if groups:
339 02c0c3fa Antony Chazapis
        try:
340 61efb530 Antony Chazapis
            request.backend.update_account_groups(request.user_uniq, v_account,
341 2715ade4 Sofia Papagiannaki
                                                  groups, replace)
342 02c0c3fa Antony Chazapis
        except NotAllowedError:
343 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
344 02c0c3fa Antony Chazapis
        except ValueError:
345 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid groups header')
346 77edd23d Antony Chazapis
    if meta or replace:
347 77edd23d Antony Chazapis
        try:
348 808cea65 Antony Chazapis
            request.backend.update_account_meta(request.user_uniq, v_account,
349 808cea65 Antony Chazapis
                                                'pithos', meta, replace)
350 77edd23d Antony Chazapis
        except NotAllowedError:
351 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
352 b956618e Antony Chazapis
    return HttpResponse(status=202)
353 b956618e Antony Chazapis
354 2715ade4 Sofia Papagiannaki
355 6b560707 Sofia Papagiannaki
@api_method('GET', format_allowed=True, user_required=True, logger=logger,
356 ee565728 Kostas Papadimitriou
            serializations=["text", "xml", "json"])
357 b956618e Antony Chazapis
def container_list(request, v_account):
358 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
359 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
360 b956618e Antony Chazapis
    #                       itemNotFound (404),
361 297513ba Antony Chazapis
    #                       forbidden (403),
362 b956618e Antony Chazapis
    #                       badRequest (400)
363 2715ade4 Sofia Papagiannaki
364 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
365 cca6c617 Antony Chazapis
    try:
366 2715ade4 Sofia Papagiannaki
        meta = request.backend.get_account_meta(
367 3b8f938b Sofia Papagiannaki
            request.user_uniq, v_account, 'pithos', until)
368 2715ade4 Sofia Papagiannaki
        groups = request.backend.get_account_groups(
369 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account)
370 2715ade4 Sofia Papagiannaki
        policy = request.backend.get_account_policy(
371 3b8f938b Sofia Papagiannaki
            request.user_uniq, v_account)
372 cca6c617 Antony Chazapis
    except NotAllowedError:
373 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
374 2715ade4 Sofia Papagiannaki
375 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
376 2715ade4 Sofia Papagiannaki
377 b956618e Antony Chazapis
    response = HttpResponse()
378 647a5f48 Antony Chazapis
    put_account_headers(response, meta, groups, policy)
379 2715ade4 Sofia Papagiannaki
380 b956618e Antony Chazapis
    marker = request.GET.get('marker')
381 f6c97079 Antony Chazapis
    limit = get_int_parameter(request.GET.get('limit'))
382 f6c97079 Antony Chazapis
    if not limit:
383 3bdb9222 Sofia Papagiannaki
        limit = settings.API_LIST_LIMIT
384 2715ade4 Sofia Papagiannaki
385 b18ef3ad Antony Chazapis
    shared = False
386 b18ef3ad Antony Chazapis
    if 'shared' in request.GET:
387 b18ef3ad Antony Chazapis
        shared = True
388 c8860319 Sofia Papagiannaki
389 c8860319 Sofia Papagiannaki
    public_requested = 'public' in request.GET
390 c8860319 Sofia Papagiannaki
    public_granted = public_requested and request.user_uniq == v_account
391 c8860319 Sofia Papagiannaki
392 c8860319 Sofia Papagiannaki
    if public_requested and not public_granted:
393 c8860319 Sofia Papagiannaki
        raise faults.Forbidden(
394 c8860319 Sofia Papagiannaki
            'PUblic container listing is not allowed to non path owners')
395 2715ade4 Sofia Papagiannaki
396 b956618e Antony Chazapis
    try:
397 2715ade4 Sofia Papagiannaki
        containers = request.backend.list_containers(
398 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account,
399 c8860319 Sofia Papagiannaki
            marker, limit, shared, until, public_granted)
400 cca6c617 Antony Chazapis
    except NotAllowedError:
401 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
402 b956618e Antony Chazapis
    except NameError:
403 b956618e Antony Chazapis
        containers = []
404 2715ade4 Sofia Papagiannaki
405 b956618e Antony Chazapis
    if request.serialization == 'text':
406 b956618e Antony Chazapis
        if len(containers) == 0:
407 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
408 b956618e Antony Chazapis
            response.status_code = 204
409 b956618e Antony Chazapis
            return response
410 b956618e Antony Chazapis
        response.status_code = 200
411 f6c97079 Antony Chazapis
        response.content = '\n'.join(containers) + '\n'
412 b956618e Antony Chazapis
        return response
413 2715ade4 Sofia Papagiannaki
414 b956618e Antony Chazapis
    container_meta = []
415 b956618e Antony Chazapis
    for x in containers:
416 f6c97079 Antony Chazapis
        try:
417 2715ade4 Sofia Papagiannaki
            meta = request.backend.get_container_meta(
418 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
419 2715ade4 Sofia Papagiannaki
                x, 'pithos', until, include_user_defined=False)
420 61efb530 Antony Chazapis
            policy = request.backend.get_container_policy(request.user_uniq,
421 2715ade4 Sofia Papagiannaki
                                                          v_account, x)
422 f6c97079 Antony Chazapis
        except NotAllowedError:
423 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
424 f6c97079 Antony Chazapis
        except NameError:
425 f6c97079 Antony Chazapis
            pass
426 f6c97079 Antony Chazapis
        else:
427 804e8fe7 Antony Chazapis
            rename_meta_key(meta, 'modified', 'last_modified')
428 2715ade4 Sofia Papagiannaki
            rename_meta_key(
429 2715ade4 Sofia Papagiannaki
                meta, 'until_timestamp', 'x_container_until_timestamp')
430 690747fe Antony Chazapis
            if policy:
431 2715ade4 Sofia Papagiannaki
                meta['X-Container-Policy'] = printable_header_dict(
432 2715ade4 Sofia Papagiannaki
                    dict([(k, v) for k, v in policy.iteritems()]))
433 f6c97079 Antony Chazapis
            container_meta.append(printable_header_dict(meta))
434 b956618e Antony Chazapis
    if request.serialization == 'xml':
435 2715ade4 Sofia Papagiannaki
        data = render_to_string('containers.xml', {'account':
436 2715ade4 Sofia Papagiannaki
                                v_account, 'containers': container_meta})
437 2715ade4 Sofia Papagiannaki
    elif request.serialization == 'json':
438 b956618e Antony Chazapis
        data = json.dumps(container_meta)
439 b956618e Antony Chazapis
    response.status_code = 200
440 b956618e Antony Chazapis
    response.content = data
441 b956618e Antony Chazapis
    return response
442 b956618e Antony Chazapis
443 2715ade4 Sofia Papagiannaki
444 65bbcd43 Christos Stavrakakis
@api_method('HEAD', user_required=True, logger=logger)
445 b956618e Antony Chazapis
def container_meta(request, v_account, v_container):
446 b956618e Antony Chazapis
    # Normal Response Codes: 204
447 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
448 b956618e Antony Chazapis
    #                       itemNotFound (404),
449 297513ba Antony Chazapis
    #                       forbidden (403),
450 b956618e Antony Chazapis
    #                       badRequest (400)
451 2715ade4 Sofia Papagiannaki
452 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
453 b956618e Antony Chazapis
    try:
454 61efb530 Antony Chazapis
        meta = request.backend.get_container_meta(request.user_uniq, v_account,
455 2715ade4 Sofia Papagiannaki
                                                  v_container, 'pithos', until)
456 67100dd2 Ilias Tsitsimpis
        meta['object_meta'] = \
457 67100dd2 Ilias Tsitsimpis
            request.backend.list_container_meta(request.user_uniq,
458 67100dd2 Ilias Tsitsimpis
                                                v_account, v_container,
459 67100dd2 Ilias Tsitsimpis
                                                'pithos', until)
460 2715ade4 Sofia Papagiannaki
        policy = request.backend.get_container_policy(
461 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account,
462 2715ade4 Sofia Papagiannaki
            v_container)
463 cca6c617 Antony Chazapis
    except NotAllowedError:
464 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
465 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
466 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
467 2715ade4 Sofia Papagiannaki
468 a8326bef Antony Chazapis
    validate_modification_preconditions(request, meta)
469 2715ade4 Sofia Papagiannaki
470 b956618e Antony Chazapis
    response = HttpResponse(status=204)
471 39593b2b Giorgos Verigakis
    put_container_headers(request, response, meta, policy)
472 b956618e Antony Chazapis
    return response
473 b956618e Antony Chazapis
474 2715ade4 Sofia Papagiannaki
475 65bbcd43 Christos Stavrakakis
@api_method('PUT', user_required=True, logger=logger)
476 b956618e Antony Chazapis
def container_create(request, v_account, v_container):
477 b956618e Antony Chazapis
    # Normal Response Codes: 201, 202
478 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
479 b956618e Antony Chazapis
    #                       itemNotFound (404),
480 297513ba Antony Chazapis
    #                       forbidden (403),
481 b956618e Antony Chazapis
    #                       badRequest (400)
482 2715ade4 Sofia Papagiannaki
483 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
484 2715ade4 Sofia Papagiannaki
485 b956618e Antony Chazapis
    try:
486 2715ade4 Sofia Papagiannaki
        request.backend.put_container(
487 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account, v_container, policy)
488 b956618e Antony Chazapis
        ret = 201
489 cca6c617 Antony Chazapis
    except NotAllowedError:
490 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
491 a8326bef Antony Chazapis
    except ValueError:
492 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid policy header')
493 7c09b0b7 Sofia Papagiannaki
    except ContainerExists:
494 b956618e Antony Chazapis
        ret = 202
495 2715ade4 Sofia Papagiannaki
496 c5d308c6 Antony Chazapis
    if ret == 202 and policy:
497 cca6c617 Antony Chazapis
        try:
498 2715ade4 Sofia Papagiannaki
            request.backend.update_container_policy(
499 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
500 2715ade4 Sofia Papagiannaki
                v_container, policy, replace=False)
501 c5d308c6 Antony Chazapis
        except NotAllowedError:
502 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
503 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
504 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container does not exist')
505 c5d308c6 Antony Chazapis
        except ValueError:
506 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid policy header')
507 78e1f8da Sofia Papagiannaki
        except QuotaError, e:
508 78e1f8da Sofia Papagiannaki
            raise faults.RequestEntityTooLarge('Quota error: %s' % e)
509 c5d308c6 Antony Chazapis
    if meta:
510 c5d308c6 Antony Chazapis
        try:
511 61efb530 Antony Chazapis
            request.backend.update_container_meta(request.user_uniq, v_account,
512 67100dd2 Ilias Tsitsimpis
                                                  v_container, 'pithos',
513 67100dd2 Ilias Tsitsimpis
                                                  meta, replace=False)
514 cca6c617 Antony Chazapis
        except NotAllowedError:
515 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
516 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
517 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container does not exist')
518 2715ade4 Sofia Papagiannaki
519 b956618e Antony Chazapis
    return HttpResponse(status=ret)
520 b956618e Antony Chazapis
521 2715ade4 Sofia Papagiannaki
522 48bb9c89 Sofia Papagiannaki
@api_method('POST', format_allowed=True, user_required=True, logger=logger,
523 48bb9c89 Sofia Papagiannaki
            lock_container_path=True)
524 b956618e Antony Chazapis
def container_update(request, v_account, v_container):
525 b956618e Antony Chazapis
    # Normal Response Codes: 202
526 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
527 b956618e Antony Chazapis
    #                       itemNotFound (404),
528 297513ba Antony Chazapis
    #                       forbidden (403),
529 b956618e Antony Chazapis
    #                       badRequest (400)
530 2715ade4 Sofia Papagiannaki
531 3ab38c43 Antony Chazapis
    meta, policy = get_container_headers(request)
532 a6eb13e9 Antony Chazapis
    replace = True
533 a6eb13e9 Antony Chazapis
    if 'update' in request.GET:
534 a6eb13e9 Antony Chazapis
        replace = False
535 3ab38c43 Antony Chazapis
    if policy:
536 3ab38c43 Antony Chazapis
        try:
537 2715ade4 Sofia Papagiannaki
            request.backend.update_container_policy(
538 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
539 2715ade4 Sofia Papagiannaki
                v_container, policy, replace)
540 3ab38c43 Antony Chazapis
        except NotAllowedError:
541 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
542 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
543 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container does not exist')
544 3ab38c43 Antony Chazapis
        except ValueError:
545 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid policy header')
546 78e1f8da Sofia Papagiannaki
        except QuotaError, e:
547 78e1f8da Sofia Papagiannaki
            raise faults.RequestEntityTooLarge('Quota error: %s' % e)
548 77edd23d Antony Chazapis
    if meta or replace:
549 77edd23d Antony Chazapis
        try:
550 61efb530 Antony Chazapis
            request.backend.update_container_meta(request.user_uniq, v_account,
551 67100dd2 Ilias Tsitsimpis
                                                  v_container, 'pithos',
552 67100dd2 Ilias Tsitsimpis
                                                  meta, replace)
553 77edd23d Antony Chazapis
        except NotAllowedError:
554 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
555 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
556 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container does not exist')
557 2715ade4 Sofia Papagiannaki
558 5144b772 Antony Chazapis
    content_length = -1
559 5144b772 Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
560 2715ade4 Sofia Papagiannaki
        content_length = get_int_parameter(
561 2715ade4 Sofia Papagiannaki
            request.META.get('CONTENT_LENGTH', 0))
562 bd08f5c0 Antony Chazapis
    content_type = request.META.get('CONTENT_TYPE')
563 5144b772 Antony Chazapis
    hashmap = []
564 67100dd2 Ilias Tsitsimpis
    if (content_type
565 67100dd2 Ilias Tsitsimpis
            and content_type == 'application/octet-stream'
566 67100dd2 Ilias Tsitsimpis
            and content_length != 0):
567 2560c061 Sofia Papagiannaki
568 2560c061 Sofia Papagiannaki
        try:
569 2560c061 Sofia Papagiannaki
            request.backend.can_write_container(request.user_uniq, v_account,
570 2560c061 Sofia Papagiannaki
                                                v_container)
571 2560c061 Sofia Papagiannaki
        except NotAllowedError:
572 2560c061 Sofia Papagiannaki
            raise faults.Forbidden('Not allowed')
573 2560c061 Sofia Papagiannaki
574 5144b772 Antony Chazapis
        for data in socket_read_iterator(request, content_length,
575 2715ade4 Sofia Papagiannaki
                                         request.backend.block_size):
576 5144b772 Antony Chazapis
            # TODO: Raise 408 (Request Timeout) if this takes too long.
577 67100dd2 Ilias Tsitsimpis
            # TODO: Raise 499 (Client Disconnect) if a length is defined
578 67100dd2 Ilias Tsitsimpis
            #       and we stop before getting this much data.
579 5144b772 Antony Chazapis
            hashmap.append(request.backend.put_block(data))
580 2715ade4 Sofia Papagiannaki
581 5144b772 Antony Chazapis
    response = HttpResponse(status=202)
582 5144b772 Antony Chazapis
    if hashmap:
583 af7bb62f Antony Chazapis
        response.content = simple_list_response(request, hashmap)
584 5144b772 Antony Chazapis
    return response
585 b956618e Antony Chazapis
586 2715ade4 Sofia Papagiannaki
587 48bb9c89 Sofia Papagiannaki
@api_method('DELETE', user_required=True, logger=logger,
588 48bb9c89 Sofia Papagiannaki
            lock_container_path=True)
589 b956618e Antony Chazapis
def container_delete(request, v_account, v_container):
590 b956618e Antony Chazapis
    # Normal Response Codes: 204
591 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
592 b956618e Antony Chazapis
    #                       conflict (409),
593 b956618e Antony Chazapis
    #                       itemNotFound (404),
594 297513ba Antony Chazapis
    #                       forbidden (403),
595 b956618e Antony Chazapis
    #                       badRequest (400)
596 dfa2d4ba Sofia Papagiannaki
    #                       requestentitytoolarge (413)
597 2715ade4 Sofia Papagiannaki
598 bbd20b55 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
599 2715ade4 Sofia Papagiannaki
600 e46b2bcf Sofia Papagiannaki
    delimiter = request.GET.get('delimiter')
601 2715ade4 Sofia Papagiannaki
602 b956618e Antony Chazapis
    try:
603 2715ade4 Sofia Papagiannaki
        request.backend.delete_container(
604 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account, v_container,
605 2715ade4 Sofia Papagiannaki
            until, delimiter=delimiter)
606 cca6c617 Antony Chazapis
    except NotAllowedError:
607 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
608 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
609 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
610 7efc9f86 Sofia Papagiannaki
    except ContainerNotEmpty:
611 bd40abfa Christos Stavrakakis
        raise faults.Conflict('Container is not empty')
612 dfa2d4ba Sofia Papagiannaki
    except QuotaError, e:
613 bd40abfa Christos Stavrakakis
        raise faults.RequestEntityTooLarge('Quota error: %s' % e)
614 b956618e Antony Chazapis
    return HttpResponse(status=204)
615 b956618e Antony Chazapis
616 2715ade4 Sofia Papagiannaki
617 6b560707 Sofia Papagiannaki
@api_method('GET', format_allowed=True, user_required=True, logger=logger,
618 ee565728 Kostas Papadimitriou
            serializations=["text", "xml", "json"])
619 b956618e Antony Chazapis
def object_list(request, v_account, v_container):
620 b956618e Antony Chazapis
    # Normal Response Codes: 200, 204
621 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
622 b956618e Antony Chazapis
    #                       itemNotFound (404),
623 297513ba Antony Chazapis
    #                       forbidden (403),
624 b956618e Antony Chazapis
    #                       badRequest (400)
625 2715ade4 Sofia Papagiannaki
626 1495b972 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
627 b956618e Antony Chazapis
    try:
628 61efb530 Antony Chazapis
        meta = request.backend.get_container_meta(request.user_uniq, v_account,
629 2715ade4 Sofia Papagiannaki
                                                  v_container, 'pithos', until)
630 67100dd2 Ilias Tsitsimpis
        meta['object_meta'] = \
631 67100dd2 Ilias Tsitsimpis
            request.backend.list_container_meta(request.user_uniq,
632 67100dd2 Ilias Tsitsimpis
                                                v_account, v_container,
633 67100dd2 Ilias Tsitsimpis
                                                'pithos', until)
634 2715ade4 Sofia Papagiannaki
        policy = request.backend.get_container_policy(
635 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account,
636 2715ade4 Sofia Papagiannaki
            v_container)
637 cca6c617 Antony Chazapis
    except NotAllowedError:
638 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
639 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
640 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
641 2715ade4 Sofia Papagiannaki
642 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
643 2715ade4 Sofia Papagiannaki
644 b956618e Antony Chazapis
    response = HttpResponse()
645 39593b2b Giorgos Verigakis
    put_container_headers(request, response, meta, policy)
646 2715ade4 Sofia Papagiannaki
647 b956618e Antony Chazapis
    path = request.GET.get('path')
648 b956618e Antony Chazapis
    prefix = request.GET.get('prefix')
649 b956618e Antony Chazapis
    delimiter = request.GET.get('delimiter')
650 2715ade4 Sofia Papagiannaki
651 b956618e Antony Chazapis
    # Path overrides prefix and delimiter.
652 b956618e Antony Chazapis
    virtual = True
653 b956618e Antony Chazapis
    if path:
654 b956618e Antony Chazapis
        prefix = path
655 b956618e Antony Chazapis
        delimiter = '/'
656 b956618e Antony Chazapis
        virtual = False
657 2715ade4 Sofia Papagiannaki
658 b956618e Antony Chazapis
    # Naming policy.
659 dcffd53e Antony Chazapis
    if prefix and delimiter and not prefix.endswith(delimiter):
660 b956618e Antony Chazapis
        prefix = prefix + delimiter
661 b956618e Antony Chazapis
    if not prefix:
662 b956618e Antony Chazapis
        prefix = ''
663 b956618e Antony Chazapis
    prefix = prefix.lstrip('/')
664 2715ade4 Sofia Papagiannaki
665 b956618e Antony Chazapis
    marker = request.GET.get('marker')
666 f6c97079 Antony Chazapis
    limit = get_int_parameter(request.GET.get('limit'))
667 f6c97079 Antony Chazapis
    if not limit:
668 3bdb9222 Sofia Papagiannaki
        limit = settings.API_LIST_LIMIT
669 2715ade4 Sofia Papagiannaki
670 22dab079 Antony Chazapis
    keys = request.GET.get('meta')
671 22dab079 Antony Chazapis
    if keys:
672 2715ade4 Sofia Papagiannaki
        keys = [smart_str(x.strip()) for x in keys.split(',')
673 2715ade4 Sofia Papagiannaki
                if x.strip() != '']
674 1713c946 Antony Chazapis
        included, excluded, opers = parse_filters(keys)
675 1713c946 Antony Chazapis
        keys = []
676 1713c946 Antony Chazapis
        keys += [format_header_key('X-Object-Meta-' + x) for x in included]
677 2715ade4 Sofia Papagiannaki
        keys += ['!' + format_header_key('X-Object-Meta-' + x)
678 2715ade4 Sofia Papagiannaki
                 for x in excluded]
679 2715ade4 Sofia Papagiannaki
        keys += ['%s%s%s' % (format_header_key(
680 2715ade4 Sofia Papagiannaki
            'X-Object-Meta-' + k), o, v) for k, o, v in opers]
681 22dab079 Antony Chazapis
    else:
682 22dab079 Antony Chazapis
        keys = []
683 2715ade4 Sofia Papagiannaki
684 b18ef3ad Antony Chazapis
    shared = False
685 b18ef3ad Antony Chazapis
    if 'shared' in request.GET:
686 b18ef3ad Antony Chazapis
        shared = True
687 8a0c7040 Georgios D. Tsoukalas
688 8a0c7040 Georgios D. Tsoukalas
    public_requested = 'public' in request.GET
689 8a0c7040 Georgios D. Tsoukalas
    public_granted = public_requested and request.user_uniq == v_account
690 2715ade4 Sofia Papagiannaki
691 c8860319 Sofia Papagiannaki
    if public_requested and not public_granted:
692 c8860319 Sofia Papagiannaki
        raise faults.Forbidden(
693 c8860319 Sofia Papagiannaki
            'PUblic object listing is not allowed to non path owners')
694 c8860319 Sofia Papagiannaki
695 b956618e Antony Chazapis
    if request.serialization == 'text':
696 371d907a Antony Chazapis
        try:
697 2715ade4 Sofia Papagiannaki
            objects = request.backend.list_objects(
698 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
699 2715ade4 Sofia Papagiannaki
                v_container, prefix, delimiter, marker,
700 2715ade4 Sofia Papagiannaki
                limit, virtual, 'pithos', keys, shared,
701 8a0c7040 Georgios D. Tsoukalas
                until, None, public_granted)
702 371d907a Antony Chazapis
        except NotAllowedError:
703 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
704 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
705 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container does not exist')
706 2715ade4 Sofia Papagiannaki
707 b956618e Antony Chazapis
        if len(objects) == 0:
708 b956618e Antony Chazapis
            # The cloudfiles python bindings expect 200 if json/xml.
709 b956618e Antony Chazapis
            response.status_code = 204
710 b956618e Antony Chazapis
            return response
711 b956618e Antony Chazapis
        response.status_code = 200
712 83dd59c5 Antony Chazapis
        response.content = '\n'.join([x[0] for x in objects]) + '\n'
713 b956618e Antony Chazapis
        return response
714 dcffd53e Antony Chazapis
715 371d907a Antony Chazapis
    try:
716 2715ade4 Sofia Papagiannaki
        objects = request.backend.list_object_meta(
717 29148653 Sofia Papagiannaki
            request.user_uniq, v_account, v_container, prefix, delimiter,
718 29148653 Sofia Papagiannaki
            marker, limit, virtual, 'pithos', keys, shared, until, None,
719 29148653 Sofia Papagiannaki
            public_granted)
720 15a96c3e Antony Chazapis
        object_permissions = {}
721 15a96c3e Antony Chazapis
        object_public = {}
722 15a96c3e Antony Chazapis
        if until is None:
723 ddea3095 Sofia Papagiannaki
            name = '/'.join((v_account, v_container, ''))
724 ddea3095 Sofia Papagiannaki
            name_idx = len(name)
725 dc88754b Nanakos Chrysostomos
            objects_bulk = []
726 67100dd2 Ilias Tsitsimpis
            for x in request.backend.list_object_permissions(
727 67100dd2 Ilias Tsitsimpis
                    request.user_uniq, v_account, v_container, prefix):
728 ddea3095 Sofia Papagiannaki
729 ddea3095 Sofia Papagiannaki
                # filter out objects which are not under the container
730 ddea3095 Sofia Papagiannaki
                if name != x[:name_idx]:
731 ddea3095 Sofia Papagiannaki
                    continue
732 dc88754b Nanakos Chrysostomos
                objects_bulk.append(x[name_idx:])
733 f759041f Ilias Tsitsimpis
734 dc88754b Nanakos Chrysostomos
            if len(objects_bulk) > 0:
735 dc88754b Nanakos Chrysostomos
                object_permissions = \
736 dc88754b Nanakos Chrysostomos
                    request.backend.get_object_permissions_bulk(
737 dc88754b Nanakos Chrysostomos
                        request.user_uniq, v_account, v_container,
738 dc88754b Nanakos Chrysostomos
                        objects_bulk)
739 8a0c7040 Georgios D. Tsoukalas
740 1a3eff62 Sofia Papagiannaki
            if request.user_uniq == v_account:
741 1a3eff62 Sofia Papagiannaki
                # Bring public information only if the request user
742 1a3eff62 Sofia Papagiannaki
                # is the object owner
743 8a0c7040 Georgios D. Tsoukalas
                for k, v in request.backend.list_object_public(
744 8a0c7040 Georgios D. Tsoukalas
                        request.user_uniq, v_account,
745 8a0c7040 Georgios D. Tsoukalas
                        v_container, prefix).iteritems():
746 8a0c7040 Georgios D. Tsoukalas
                    object_public[k[name_idx:]] = v
747 371d907a Antony Chazapis
    except NotAllowedError:
748 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
749 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
750 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
751 2715ade4 Sofia Papagiannaki
752 b956618e Antony Chazapis
    object_meta = []
753 371d907a Antony Chazapis
    for meta in objects:
754 469d0997 Georgios D. Tsoukalas
        if TRANSLATE_UUIDS:
755 469d0997 Georgios D. Tsoukalas
            modified_by = meta.get('modified_by')
756 469d0997 Georgios D. Tsoukalas
            if modified_by:
757 469d0997 Georgios D. Tsoukalas
                l = retrieve_displaynames(
758 67100dd2 Ilias Tsitsimpis
                    getattr(request, 'token', None), [meta['modified_by']])
759 469d0997 Georgios D. Tsoukalas
                if l is not None and len(l) == 1:
760 469d0997 Georgios D. Tsoukalas
                    meta['modified_by'] = l[0]
761 469d0997 Georgios D. Tsoukalas
762 371d907a Antony Chazapis
        if len(meta) == 1:
763 b956618e Antony Chazapis
            # Virtual objects/directories.
764 371d907a Antony Chazapis
            object_meta.append(meta)
765 83dd59c5 Antony Chazapis
        else:
766 2715ade4 Sofia Papagiannaki
            rename_meta_key(
767 2715ade4 Sofia Papagiannaki
                meta, 'hash', 'x_object_hash')  # Will be replaced by checksum.
768 371d907a Antony Chazapis
            rename_meta_key(meta, 'checksum', 'hash')
769 371d907a Antony Chazapis
            rename_meta_key(meta, 'type', 'content_type')
770 371d907a Antony Chazapis
            rename_meta_key(meta, 'uuid', 'x_object_uuid')
771 371d907a Antony Chazapis
            if until is not None and 'modified' in meta:
772 371d907a Antony Chazapis
                del(meta['modified'])
773 038f1ae9 Antony Chazapis
            else:
774 804e8fe7 Antony Chazapis
                rename_meta_key(meta, 'modified', 'last_modified')
775 371d907a Antony Chazapis
            rename_meta_key(meta, 'modified_by', 'x_object_modified_by')
776 371d907a Antony Chazapis
            rename_meta_key(meta, 'version', 'x_object_version')
777 2715ade4 Sofia Papagiannaki
            rename_meta_key(
778 2715ade4 Sofia Papagiannaki
                meta, 'version_timestamp', 'x_object_version_timestamp')
779 15a96c3e Antony Chazapis
            permissions = object_permissions.get(meta['name'], None)
780 15a96c3e Antony Chazapis
            if permissions:
781 2715ade4 Sofia Papagiannaki
                update_sharing_meta(request, permissions, v_account,
782 2715ade4 Sofia Papagiannaki
                                    v_container, meta['name'], meta)
783 8a0c7040 Georgios D. Tsoukalas
            public_url = object_public.get(meta['name'], None)
784 1a3eff62 Sofia Papagiannaki
            if request.user_uniq == v_account:
785 1a3eff62 Sofia Papagiannaki
                # Return public information only if the request user
786 1a3eff62 Sofia Papagiannaki
                # is the object owner
787 8a0c7040 Georgios D. Tsoukalas
                update_public_meta(public_url, meta)
788 371d907a Antony Chazapis
            object_meta.append(printable_header_dict(meta))
789 469d0997 Georgios D. Tsoukalas
790 b956618e Antony Chazapis
    if request.serialization == 'xml':
791 2715ade4 Sofia Papagiannaki
        data = render_to_string(
792 2715ade4 Sofia Papagiannaki
            'objects.xml', {'container': v_container, 'objects': object_meta})
793 2715ade4 Sofia Papagiannaki
    elif request.serialization == 'json':
794 c48acbfd Sofia Papagiannaki
        data = json.dumps(object_meta, default=json_encode_decimal)
795 b956618e Antony Chazapis
    response.status_code = 200
796 b956618e Antony Chazapis
    response.content = data
797 b956618e Antony Chazapis
    return response
798 b956618e Antony Chazapis
799 2715ade4 Sofia Papagiannaki
800 65bbcd43 Christos Stavrakakis
@api_method('HEAD', user_required=True, logger=logger)
801 b956618e Antony Chazapis
def object_meta(request, v_account, v_container, v_object):
802 b956618e Antony Chazapis
    # Normal Response Codes: 204
803 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
804 b956618e Antony Chazapis
    #                       itemNotFound (404),
805 297513ba Antony Chazapis
    #                       forbidden (403),
806 b956618e Antony Chazapis
    #                       badRequest (400)
807 2715ade4 Sofia Papagiannaki
808 104626e3 Antony Chazapis
    version = request.GET.get('version')
809 b956618e Antony Chazapis
    try:
810 61efb530 Antony Chazapis
        meta = request.backend.get_object_meta(request.user_uniq, v_account,
811 67100dd2 Ilias Tsitsimpis
                                               v_container, v_object,
812 67100dd2 Ilias Tsitsimpis
                                               'pithos', version)
813 e8886082 Antony Chazapis
        if version is None:
814 2715ade4 Sofia Papagiannaki
            permissions = request.backend.get_object_permissions(
815 2715ade4 Sofia Papagiannaki
                request.user_uniq,
816 2715ade4 Sofia Papagiannaki
                v_account, v_container, v_object)
817 2715ade4 Sofia Papagiannaki
            public = request.backend.get_object_public(
818 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
819 2715ade4 Sofia Papagiannaki
                v_container, v_object)
820 e8886082 Antony Chazapis
        else:
821 e8886082 Antony Chazapis
            permissions = None
822 e0f916bb Antony Chazapis
            public = None
823 cca6c617 Antony Chazapis
    except NotAllowedError:
824 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
825 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
826 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Object does not exist')
827 7efc9f86 Sofia Papagiannaki
    except VersionNotExists:
828 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Version does not exist')
829 2715ade4 Sofia Papagiannaki
830 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
831 2715ade4 Sofia Papagiannaki
    update_sharing_meta(
832 2715ade4 Sofia Papagiannaki
        request, permissions, v_account, v_container, v_object, meta)
833 6fe32fb6 Sofia Papagiannaki
    if request.user_uniq == v_account:
834 6fe32fb6 Sofia Papagiannaki
        update_public_meta(public, meta)
835 2715ade4 Sofia Papagiannaki
836 a8326bef Antony Chazapis
    # Evaluate conditions.
837 a8326bef Antony Chazapis
    validate_modification_preconditions(request, meta)
838 a8326bef Antony Chazapis
    try:
839 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
840 bd40abfa Christos Stavrakakis
    except faults.NotModified:
841 a8326bef Antony Chazapis
        response = HttpResponse(status=304)
842 133e3fcf Sofia Papagiannaki
        response['ETag'] = meta['hash'] if not UPDATE_MD5 else meta['checksum']
843 a8326bef Antony Chazapis
        return response
844 2715ade4 Sofia Papagiannaki
845 cb146cf9 Antony Chazapis
    response = HttpResponse(status=200)
846 469d0997 Georgios D. Tsoukalas
    put_object_headers(response, meta, token=getattr(request, 'token', None))
847 b956618e Antony Chazapis
    return response
848 b956618e Antony Chazapis
849 2715ade4 Sofia Papagiannaki
850 65bbcd43 Christos Stavrakakis
@api_method('GET', format_allowed=True, user_required=True, logger=logger)
851 b956618e Antony Chazapis
def object_read(request, v_account, v_container, v_object):
852 63de12cf Sofia Papagiannaki
    return _object_read(request, v_account, v_container, v_object)
853 63de12cf Sofia Papagiannaki
854 63de12cf Sofia Papagiannaki
855 63de12cf Sofia Papagiannaki
def _object_read(request, v_account, v_container, v_object):
856 b956618e Antony Chazapis
    # Normal Response Codes: 200, 206
857 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
858 b956618e Antony Chazapis
    #                       rangeNotSatisfiable (416),
859 b956618e Antony Chazapis
    #                       preconditionFailed (412),
860 b956618e Antony Chazapis
    #                       itemNotFound (404),
861 297513ba Antony Chazapis
    #                       forbidden (403),
862 b956618e Antony Chazapis
    #                       badRequest (400),
863 b956618e Antony Chazapis
    #                       notModified (304)
864 2715ade4 Sofia Papagiannaki
865 104626e3 Antony Chazapis
    version = request.GET.get('version')
866 2715ade4 Sofia Papagiannaki
867 e8886082 Antony Chazapis
    # Reply with the version list. Do this first, as the object may be deleted.
868 104626e3 Antony Chazapis
    if version == 'list':
869 e8886082 Antony Chazapis
        if request.serialization == 'text':
870 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('No format specified for version list.')
871 2715ade4 Sofia Papagiannaki
872 cca6c617 Antony Chazapis
        try:
873 61efb530 Antony Chazapis
            v = request.backend.list_versions(request.user_uniq, v_account,
874 2715ade4 Sofia Papagiannaki
                                              v_container, v_object)
875 cca6c617 Antony Chazapis
        except NotAllowedError:
876 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
877 02b8c298 Sofia Papagiannaki
        except ItemNotExists:
878 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Object does not exist')
879 cca6c617 Antony Chazapis
        d = {'versions': v}
880 e8886082 Antony Chazapis
        if request.serialization == 'xml':
881 e8886082 Antony Chazapis
            d['object'] = v_object
882 e8886082 Antony Chazapis
            data = render_to_string('versions.xml', d)
883 2715ade4 Sofia Papagiannaki
        elif request.serialization == 'json':
884 c48acbfd Sofia Papagiannaki
            data = json.dumps(d, default=json_encode_decimal)
885 2715ade4 Sofia Papagiannaki
886 e8886082 Antony Chazapis
        response = HttpResponse(data, status=200)
887 e8886082 Antony Chazapis
        response['Content-Length'] = len(data)
888 e8886082 Antony Chazapis
        return response
889 2715ade4 Sofia Papagiannaki
890 b956618e Antony Chazapis
    try:
891 61efb530 Antony Chazapis
        meta = request.backend.get_object_meta(request.user_uniq, v_account,
892 67100dd2 Ilias Tsitsimpis
                                               v_container, v_object,
893 67100dd2 Ilias Tsitsimpis
                                               'pithos', version)
894 e8886082 Antony Chazapis
        if version is None:
895 2715ade4 Sofia Papagiannaki
            permissions = request.backend.get_object_permissions(
896 2715ade4 Sofia Papagiannaki
                request.user_uniq,
897 2715ade4 Sofia Papagiannaki
                v_account, v_container, v_object)
898 2715ade4 Sofia Papagiannaki
            public = request.backend.get_object_public(
899 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
900 2715ade4 Sofia Papagiannaki
                v_container, v_object)
901 e8886082 Antony Chazapis
        else:
902 e8886082 Antony Chazapis
            permissions = None
903 e0f916bb Antony Chazapis
            public = None
904 cca6c617 Antony Chazapis
    except NotAllowedError:
905 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
906 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
907 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Object does not exist')
908 7efc9f86 Sofia Papagiannaki
    except VersionNotExists:
909 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Version does not exist')
910 2715ade4 Sofia Papagiannaki
911 8cb45c13 Antony Chazapis
    update_manifest_meta(request, v_account, meta)
912 2715ade4 Sofia Papagiannaki
    update_sharing_meta(
913 2715ade4 Sofia Papagiannaki
        request, permissions, v_account, v_container, v_object, meta)
914 6fe32fb6 Sofia Papagiannaki
    if request.user_uniq == v_account:
915 6fe32fb6 Sofia Papagiannaki
        update_public_meta(public, meta)
916 2715ade4 Sofia Papagiannaki
917 22dab079 Antony Chazapis
    # Evaluate conditions.
918 b956618e Antony Chazapis
    validate_modification_preconditions(request, meta)
919 22dab079 Antony Chazapis
    try:
920 22dab079 Antony Chazapis
        validate_matching_preconditions(request, meta)
921 bd40abfa Christos Stavrakakis
    except faults.NotModified:
922 22dab079 Antony Chazapis
        response = HttpResponse(status=304)
923 133e3fcf Sofia Papagiannaki
        response['ETag'] = meta['hash'] if not UPDATE_MD5 else meta['checksum']
924 22dab079 Antony Chazapis
        return response
925 2715ade4 Sofia Papagiannaki
926 2fd10ff4 Antony Chazapis
    hashmap_reply = False
927 2fd10ff4 Antony Chazapis
    if 'hashmap' in request.GET and request.serialization != 'text':
928 2fd10ff4 Antony Chazapis
        hashmap_reply = True
929 2715ade4 Sofia Papagiannaki
930 8cb45c13 Antony Chazapis
    sizes = []
931 8cb45c13 Antony Chazapis
    hashmaps = []
932 2fd10ff4 Antony Chazapis
    if 'X-Object-Manifest' in meta and not hashmap_reply:
933 8cb45c13 Antony Chazapis
        try:
934 2715ade4 Sofia Papagiannaki
            src_container, src_name = split_container_object_string(
935 2715ade4 Sofia Papagiannaki
                '/' + meta['X-Object-Manifest'])
936 2715ade4 Sofia Papagiannaki
            objects = request.backend.list_objects(
937 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
938 2715ade4 Sofia Papagiannaki
                src_container, prefix=src_name, virtual=False)
939 cca6c617 Antony Chazapis
        except NotAllowedError:
940 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
941 8cb45c13 Antony Chazapis
        except ValueError:
942 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid X-Object-Manifest header')
943 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
944 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container does not exist')
945 2715ade4 Sofia Papagiannaki
946 8cb45c13 Antony Chazapis
        try:
947 8cb45c13 Antony Chazapis
            for x in objects:
948 67100dd2 Ilias Tsitsimpis
                s, h = \
949 29148653 Sofia Papagiannaki
                    request.backend.get_object_hashmap(
950 29148653 Sofia Papagiannaki
                        request.user_uniq, v_account, src_container, x[0],
951 29148653 Sofia Papagiannaki
                        x[1])
952 8cb45c13 Antony Chazapis
                sizes.append(s)
953 8cb45c13 Antony Chazapis
                hashmaps.append(h)
954 cca6c617 Antony Chazapis
        except NotAllowedError:
955 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
956 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
957 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Object does not exist')
958 7efc9f86 Sofia Papagiannaki
        except VersionNotExists:
959 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Version does not exist')
960 8cb45c13 Antony Chazapis
    else:
961 8cb45c13 Antony Chazapis
        try:
962 2715ade4 Sofia Papagiannaki
            s, h = request.backend.get_object_hashmap(
963 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
964 2715ade4 Sofia Papagiannaki
                v_container, v_object, version)
965 8cb45c13 Antony Chazapis
            sizes.append(s)
966 8cb45c13 Antony Chazapis
            hashmaps.append(h)
967 cca6c617 Antony Chazapis
        except NotAllowedError:
968 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
969 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
970 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Object does not exist')
971 7efc9f86 Sofia Papagiannaki
        except VersionNotExists:
972 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Version does not exist')
973 2715ade4 Sofia Papagiannaki
974 22dab079 Antony Chazapis
    # Reply with the hashmap.
975 2fd10ff4 Antony Chazapis
    if hashmap_reply:
976 8cb45c13 Antony Chazapis
        size = sum(sizes)
977 8cb45c13 Antony Chazapis
        hashmap = sum(hashmaps, [])
978 39593b2b Giorgos Verigakis
        d = {
979 39593b2b Giorgos Verigakis
            'block_size': request.backend.block_size,
980 39593b2b Giorgos Verigakis
            'block_hash': request.backend.hash_algorithm,
981 39593b2b Giorgos Verigakis
            'bytes': size,
982 39593b2b Giorgos Verigakis
            'hashes': hashmap}
983 22dab079 Antony Chazapis
        if request.serialization == 'xml':
984 6bc372a4 Antony Chazapis
            d['object'] = v_object
985 6bc372a4 Antony Chazapis
            data = render_to_string('hashes.xml', d)
986 2715ade4 Sofia Papagiannaki
        elif request.serialization == 'json':
987 6bc372a4 Antony Chazapis
            data = json.dumps(d)
988 2715ade4 Sofia Papagiannaki
989 22dab079 Antony Chazapis
        response = HttpResponse(data, status=200)
990 469d0997 Georgios D. Tsoukalas
        put_object_headers(
991 67100dd2 Ilias Tsitsimpis
            response, meta, token=getattr(request, 'token', None))
992 22dab079 Antony Chazapis
        response['Content-Length'] = len(data)
993 22dab079 Antony Chazapis
        return response
994 2715ade4 Sofia Papagiannaki
995 2715ade4 Sofia Papagiannaki
    request.serialization = 'text'  # Unset.
996 8cb45c13 Antony Chazapis
    return object_data_response(request, sizes, hashmaps, meta)
997 b956618e Antony Chazapis
998 2715ade4 Sofia Papagiannaki
999 b90584d0 Sofia Papagiannaki
@api_method('PUT', format_allowed=True, user_required=True, logger=logger,
1000 b90584d0 Sofia Papagiannaki
            lock_container_path=True)
1001 b956618e Antony Chazapis
def object_write(request, v_account, v_container, v_object):
1002 b956618e Antony Chazapis
    # Normal Response Codes: 201
1003 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1004 b956618e Antony Chazapis
    #                       unprocessableEntity (422),
1005 b956618e Antony Chazapis
    #                       lengthRequired (411),
1006 3436eeb0 Antony Chazapis
    #                       conflict (409),
1007 b956618e Antony Chazapis
    #                       itemNotFound (404),
1008 297513ba Antony Chazapis
    #                       forbidden (403),
1009 b956618e Antony Chazapis
    #                       badRequest (400)
1010 dfa2d4ba Sofia Papagiannaki
    #                       requestentitytoolarge (413)
1011 2715ade4 Sofia Papagiannaki
1012 a8326bef Antony Chazapis
    # Evaluate conditions.
1013 67100dd2 Ilias Tsitsimpis
    if (request.META.get('HTTP_IF_MATCH')
1014 67100dd2 Ilias Tsitsimpis
            or request.META.get('HTTP_IF_NONE_MATCH')):
1015 a8326bef Antony Chazapis
        try:
1016 2715ade4 Sofia Papagiannaki
            meta = request.backend.get_object_meta(
1017 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
1018 2715ade4 Sofia Papagiannaki
                v_container, v_object, 'pithos')
1019 a8326bef Antony Chazapis
        except NotAllowedError:
1020 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1021 a8326bef Antony Chazapis
        except NameError:
1022 a8326bef Antony Chazapis
            meta = {}
1023 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
1024 2715ade4 Sofia Papagiannaki
1025 88283e9e Antony Chazapis
    copy_from = request.META.get('HTTP_X_COPY_FROM')
1026 88283e9e Antony Chazapis
    move_from = request.META.get('HTTP_X_MOVE_FROM')
1027 b956618e Antony Chazapis
    if copy_from or move_from:
1028 4d15c94e Sofia Papagiannaki
        delimiter = request.GET.get('delimiter')
1029 2715ade4 Sofia Papagiannaki
        content_length = get_content_length(request)  # Required by the API.
1030 2715ade4 Sofia Papagiannaki
1031 88283e9e Antony Chazapis
        src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
1032 f483f65d Sofia Papagiannaki
1033 79bb41b7 Antony Chazapis
        if not src_account:
1034 61efb530 Antony Chazapis
            src_account = request.user_uniq
1035 f483f65d Sofia Papagiannaki
        else:
1036 f483f65d Sofia Papagiannaki
            if TRANSLATE_UUIDS:
1037 f483f65d Sofia Papagiannaki
                try:
1038 29148653 Sofia Papagiannaki
                    src_account = retrieve_uuid(
1039 29148653 Sofia Papagiannaki
                        getattr(request, 'token', None), src_account)
1040 f483f65d Sofia Papagiannaki
                except ItemNotExists:
1041 bd40abfa Christos Stavrakakis
                    faults.ItemNotFound('Invalid source account')
1042 f483f65d Sofia Papagiannaki
1043 b956618e Antony Chazapis
        if move_from:
1044 83dd59c5 Antony Chazapis
            try:
1045 2715ade4 Sofia Papagiannaki
                src_container, src_name = split_container_object_string(
1046 2715ade4 Sofia Papagiannaki
                    move_from)
1047 83dd59c5 Antony Chazapis
            except ValueError:
1048 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Invalid X-Move-From header')
1049 2715ade4 Sofia Papagiannaki
            version_id = copy_or_move_object(
1050 2715ade4 Sofia Papagiannaki
                request, src_account, src_container, src_name,
1051 67100dd2 Ilias Tsitsimpis
                v_account, v_container, v_object,
1052 67100dd2 Ilias Tsitsimpis
                move=True, delimiter=delimiter)
1053 b956618e Antony Chazapis
        else:
1054 83dd59c5 Antony Chazapis
            try:
1055 2715ade4 Sofia Papagiannaki
                src_container, src_name = split_container_object_string(
1056 2715ade4 Sofia Papagiannaki
                    copy_from)
1057 83dd59c5 Antony Chazapis
            except ValueError:
1058 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Invalid X-Copy-From header')
1059 2715ade4 Sofia Papagiannaki
            version_id = copy_or_move_object(
1060 2715ade4 Sofia Papagiannaki
                request, src_account, src_container, src_name,
1061 67100dd2 Ilias Tsitsimpis
                v_account, v_container, v_object,
1062 67100dd2 Ilias Tsitsimpis
                move=False, delimiter=delimiter)
1063 7dd293a0 Antony Chazapis
        response = HttpResponse(status=201)
1064 7dd293a0 Antony Chazapis
        response['X-Object-Version'] = version_id
1065 7dd293a0 Antony Chazapis
        return response
1066 2715ade4 Sofia Papagiannaki
1067 66ce2ca5 Antony Chazapis
    content_type, meta, permissions, public = get_object_headers(request)
1068 b956618e Antony Chazapis
    content_length = -1
1069 b956618e Antony Chazapis
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
1070 22dab079 Antony Chazapis
        content_length = get_content_length(request)
1071 b956618e Antony Chazapis
    # Should be BadRequest, but API says otherwise.
1072 dcffd53e Antony Chazapis
    if content_type is None:
1073 bd40abfa Christos Stavrakakis
        raise faults.LengthRequired('Missing Content-Type header')
1074 2715ade4 Sofia Papagiannaki
1075 f9f15f92 Antony Chazapis
    if 'hashmap' in request.GET:
1076 f9f15f92 Antony Chazapis
        if request.serialization not in ('json', 'xml'):
1077 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid hashmap format')
1078 2715ade4 Sofia Papagiannaki
1079 76985443 Sofia Papagiannaki
        data = ''
1080 39593b2b Giorgos Verigakis
        for block in socket_read_iterator(request, content_length,
1081 2715ade4 Sofia Papagiannaki
                                          request.backend.block_size):
1082 76985443 Sofia Papagiannaki
            data = '%s%s' % (data, block)
1083 2715ade4 Sofia Papagiannaki
1084 32a437b1 Sofia Papagiannaki
        if request.serialization == 'json':
1085 32a437b1 Sofia Papagiannaki
            d = json.loads(data)
1086 32a437b1 Sofia Papagiannaki
            if not hasattr(d, '__getitem__'):
1087 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Invalid data formating')
1088 32a437b1 Sofia Papagiannaki
            try:
1089 32a437b1 Sofia Papagiannaki
                hashmap = d['hashes']
1090 f899c7b4 Antony Chazapis
                size = int(d['bytes'])
1091 f899c7b4 Antony Chazapis
            except:
1092 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Invalid data formatting')
1093 32a437b1 Sofia Papagiannaki
        elif request.serialization == 'xml':
1094 32a437b1 Sofia Papagiannaki
            try:
1095 32a437b1 Sofia Papagiannaki
                xml = minidom.parseString(data)
1096 32a437b1 Sofia Papagiannaki
                obj = xml.getElementsByTagName('object')[0]
1097 f899c7b4 Antony Chazapis
                size = int(obj.attributes['bytes'].value)
1098 2715ade4 Sofia Papagiannaki
1099 32a437b1 Sofia Papagiannaki
                hashes = xml.getElementsByTagName('hash')
1100 32a437b1 Sofia Papagiannaki
                hashmap = []
1101 32a437b1 Sofia Papagiannaki
                for hash in hashes:
1102 32a437b1 Sofia Papagiannaki
                    hashmap.append(hash.firstChild.data)
1103 f899c7b4 Antony Chazapis
            except:
1104 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Invalid data formatting')
1105 2715ade4 Sofia Papagiannaki
1106 2715ade4 Sofia Papagiannaki
        checksum = ''  # Do not set to None (will copy previous value).
1107 76985443 Sofia Papagiannaki
    else:
1108 133e3fcf Sofia Papagiannaki
        etag = request.META.get('HTTP_ETAG')
1109 133e3fcf Sofia Papagiannaki
        checksum_compute = Checksum() if etag or UPDATE_MD5 else NoChecksum()
1110 76985443 Sofia Papagiannaki
        size = 0
1111 76985443 Sofia Papagiannaki
        hashmap = []
1112 39593b2b Giorgos Verigakis
        for data in socket_read_iterator(request, content_length,
1113 2715ade4 Sofia Papagiannaki
                                         request.backend.block_size):
1114 76985443 Sofia Papagiannaki
            # TODO: Raise 408 (Request Timeout) if this takes too long.
1115 67100dd2 Ilias Tsitsimpis
            # TODO: Raise 499 (Client Disconnect) if a length is defined
1116 67100dd2 Ilias Tsitsimpis
            #       and we stop before getting this much data.
1117 76985443 Sofia Papagiannaki
            size += len(data)
1118 39593b2b Giorgos Verigakis
            hashmap.append(request.backend.put_block(data))
1119 133e3fcf Sofia Papagiannaki
            checksum_compute.update(data)
1120 2715ade4 Sofia Papagiannaki
1121 133e3fcf Sofia Papagiannaki
        checksum = checksum_compute.hexdigest()
1122 33b4e4a6 Antony Chazapis
        if etag and parse_etags(etag)[0].lower() != checksum:
1123 bd40abfa Christos Stavrakakis
            raise faults.UnprocessableEntity('Object ETag does not match')
1124 2715ade4 Sofia Papagiannaki
1125 22dab079 Antony Chazapis
    try:
1126 133e3fcf Sofia Papagiannaki
        version_id, merkle = request.backend.update_object_hashmap(
1127 133e3fcf Sofia Papagiannaki
            request.user_uniq, v_account, v_container, v_object, size,
1128 133e3fcf Sofia Papagiannaki
            content_type, hashmap, checksum, 'pithos', meta, True, permissions
1129 133e3fcf Sofia Papagiannaki
        )
1130 cca6c617 Antony Chazapis
    except NotAllowedError:
1131 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
1132 76985443 Sofia Papagiannaki
    except IndexError, e:
1133 e9d94604 Sofia Papagiannaki
        missing_blocks = e.data
1134 e9d94604 Sofia Papagiannaki
        response = HttpResponse(status=409)
1135 e9d94604 Sofia Papagiannaki
        response.content = simple_list_response(request, missing_blocks)
1136 e9d94604 Sofia Papagiannaki
        return response
1137 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
1138 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
1139 3436eeb0 Antony Chazapis
    except ValueError:
1140 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid sharing header')
1141 ccfd4e44 Sofia Papagiannaki
    except QuotaError, e:
1142 bd40abfa Christos Stavrakakis
        raise faults.RequestEntityTooLarge('Quota error: %s' % e)
1143 3a5994a8 Sofia Papagiannaki
    except InvalidHash, e:
1144 3a5994a8 Sofia Papagiannaki
        raise faults.BadRequest('Invalid hash: %s' % e)
1145 8efd183f Antony Chazapis
    if not checksum and UPDATE_MD5:
1146 cddcf432 chazapis
        # Update the MD5 after the hashmap, as there may be missing hashes.
1147 b3155065 Antony Chazapis
        checksum = hashmap_md5(request.backend, hashmap, size)
1148 cddcf432 chazapis
        try:
1149 b3155065 Antony Chazapis
            request.backend.update_object_checksum(request.user_uniq,
1150 67100dd2 Ilias Tsitsimpis
                                                   v_account, v_container,
1151 67100dd2 Ilias Tsitsimpis
                                                   v_object, version_id,
1152 67100dd2 Ilias Tsitsimpis
                                                   checksum)
1153 cddcf432 chazapis
        except NotAllowedError:
1154 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1155 e0f916bb Antony Chazapis
    if public is not None:
1156 e0f916bb Antony Chazapis
        try:
1157 61efb530 Antony Chazapis
            request.backend.update_object_public(request.user_uniq, v_account,
1158 2715ade4 Sofia Papagiannaki
                                                 v_container, v_object, public)
1159 e0f916bb Antony Chazapis
        except NotAllowedError:
1160 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1161 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
1162 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Object does not exist')
1163 2715ade4 Sofia Papagiannaki
1164 1993fea9 Antony Chazapis
    response = HttpResponse(status=201)
1165 133e3fcf Sofia Papagiannaki
    response['ETag'] = merkle if not UPDATE_MD5 else checksum
1166 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1167 b956618e Antony Chazapis
    return response
1168 b956618e Antony Chazapis
1169 2715ade4 Sofia Papagiannaki
1170 29148653 Sofia Papagiannaki
@api_method('POST', user_required=True, logger=logger,
1171 29148653 Sofia Papagiannaki
            lock_container_path=True)
1172 1d5c57d3 Antony Chazapis
def object_write_form(request, v_account, v_container, v_object):
1173 1d5c57d3 Antony Chazapis
    # Normal Response Codes: 201
1174 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1175 1d5c57d3 Antony Chazapis
    #                       itemNotFound (404),
1176 297513ba Antony Chazapis
    #                       forbidden (403),
1177 1d5c57d3 Antony Chazapis
    #                       badRequest (400)
1178 dfa2d4ba Sofia Papagiannaki
    #                       requestentitytoolarge (413)
1179 2715ade4 Sofia Papagiannaki
1180 817890f2 Antony Chazapis
    request.upload_handlers = [SaveToBackendHandler(request)]
1181 2715ade4 Sofia Papagiannaki
    if 'X-Object-Data' not in request.FILES:
1182 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Missing X-Object-Data field')
1183 1d5c57d3 Antony Chazapis
    file = request.FILES['X-Object-Data']
1184 2715ade4 Sofia Papagiannaki
1185 33b4e4a6 Antony Chazapis
    checksum = file.etag
1186 1d5c57d3 Antony Chazapis
    try:
1187 133e3fcf Sofia Papagiannaki
        version_id, merkle = request.backend.update_object_hashmap(
1188 133e3fcf Sofia Papagiannaki
            request.user_uniq, v_account, v_container, v_object, file.size,
1189 133e3fcf Sofia Papagiannaki
            file.content_type, file.hashmap, checksum, 'pithos', {}, True
1190 133e3fcf Sofia Papagiannaki
        )
1191 1d5c57d3 Antony Chazapis
    except NotAllowedError:
1192 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
1193 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
1194 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
1195 ccfd4e44 Sofia Papagiannaki
    except QuotaError, e:
1196 bd40abfa Christos Stavrakakis
        raise faults.RequestEntityTooLarge('Quota error: %s' % e)
1197 2715ade4 Sofia Papagiannaki
1198 1d5c57d3 Antony Chazapis
    response = HttpResponse(status=201)
1199 133e3fcf Sofia Papagiannaki
    response['ETag'] = merkle if not UPDATE_MD5 else checksum
1200 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1201 33b4e4a6 Antony Chazapis
    response.content = checksum
1202 1d5c57d3 Antony Chazapis
    return response
1203 1d5c57d3 Antony Chazapis
1204 2715ade4 Sofia Papagiannaki
1205 b90584d0 Sofia Papagiannaki
@api_method('COPY', format_allowed=True, user_required=True, logger=logger,
1206 b90584d0 Sofia Papagiannaki
            lock_container_path=True)
1207 b956618e Antony Chazapis
def object_copy(request, v_account, v_container, v_object):
1208 b956618e Antony Chazapis
    # Normal Response Codes: 201
1209 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1210 b956618e Antony Chazapis
    #                       itemNotFound (404),
1211 297513ba Antony Chazapis
    #                       forbidden (403),
1212 b956618e Antony Chazapis
    #                       badRequest (400)
1213 dfa2d4ba Sofia Papagiannaki
    #                       requestentitytoolarge (413)
1214 2715ade4 Sofia Papagiannaki
1215 88283e9e Antony Chazapis
    dest_account = request.META.get('HTTP_DESTINATION_ACCOUNT')
1216 79bb41b7 Antony Chazapis
    if not dest_account:
1217 61efb530 Antony Chazapis
        dest_account = request.user_uniq
1218 88283e9e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
1219 b956618e Antony Chazapis
    if not dest_path:
1220 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Missing Destination header')
1221 83dd59c5 Antony Chazapis
    try:
1222 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
1223 83dd59c5 Antony Chazapis
    except ValueError:
1224 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid Destination header')
1225 2715ade4 Sofia Papagiannaki
1226 a8326bef Antony Chazapis
    # Evaluate conditions.
1227 67100dd2 Ilias Tsitsimpis
    if (request.META.get('HTTP_IF_MATCH')
1228 67100dd2 Ilias Tsitsimpis
            or request.META.get('HTTP_IF_NONE_MATCH')):
1229 a8326bef Antony Chazapis
        src_version = request.META.get('HTTP_X_SOURCE_VERSION')
1230 a8326bef Antony Chazapis
        try:
1231 2715ade4 Sofia Papagiannaki
            meta = request.backend.get_object_meta(
1232 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
1233 2715ade4 Sofia Papagiannaki
                v_container, v_object, 'pithos', src_version)
1234 a8326bef Antony Chazapis
        except NotAllowedError:
1235 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1236 7efc9f86 Sofia Papagiannaki
        except (ItemNotExists, VersionNotExists):
1237 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container or object does not exist')
1238 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
1239 2715ade4 Sofia Papagiannaki
1240 4d15c94e Sofia Papagiannaki
    delimiter = request.GET.get('delimiter')
1241 2715ade4 Sofia Papagiannaki
1242 79bb41b7 Antony Chazapis
    version_id = copy_or_move_object(request, v_account, v_container, v_object,
1243 67100dd2 Ilias Tsitsimpis
                                     dest_account, dest_container, dest_name,
1244 67100dd2 Ilias Tsitsimpis
                                     move=False, delimiter=delimiter)
1245 7dd293a0 Antony Chazapis
    response = HttpResponse(status=201)
1246 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1247 7dd293a0 Antony Chazapis
    return response
1248 b956618e Antony Chazapis
1249 2715ade4 Sofia Papagiannaki
1250 b90584d0 Sofia Papagiannaki
@api_method('MOVE', format_allowed=True, user_required=True, logger=logger,
1251 b90584d0 Sofia Papagiannaki
            lock_container_path=True)
1252 b956618e Antony Chazapis
def object_move(request, v_account, v_container, v_object):
1253 b956618e Antony Chazapis
    # Normal Response Codes: 201
1254 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1255 b956618e Antony Chazapis
    #                       itemNotFound (404),
1256 297513ba Antony Chazapis
    #                       forbidden (403),
1257 b956618e Antony Chazapis
    #                       badRequest (400)
1258 dfa2d4ba Sofia Papagiannaki
    #                       requestentitytoolarge (413)
1259 2715ade4 Sofia Papagiannaki
1260 88283e9e Antony Chazapis
    dest_account = request.META.get('HTTP_DESTINATION_ACCOUNT')
1261 79bb41b7 Antony Chazapis
    if not dest_account:
1262 61efb530 Antony Chazapis
        dest_account = request.user_uniq
1263 88283e9e Antony Chazapis
    dest_path = request.META.get('HTTP_DESTINATION')
1264 b956618e Antony Chazapis
    if not dest_path:
1265 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Missing Destination header')
1266 83dd59c5 Antony Chazapis
    try:
1267 83dd59c5 Antony Chazapis
        dest_container, dest_name = split_container_object_string(dest_path)
1268 83dd59c5 Antony Chazapis
    except ValueError:
1269 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid Destination header')
1270 2715ade4 Sofia Papagiannaki
1271 a8326bef Antony Chazapis
    # Evaluate conditions.
1272 67100dd2 Ilias Tsitsimpis
    if (request.META.get('HTTP_IF_MATCH')
1273 67100dd2 Ilias Tsitsimpis
            or request.META.get('HTTP_IF_NONE_MATCH')):
1274 a8326bef Antony Chazapis
        try:
1275 2715ade4 Sofia Papagiannaki
            meta = request.backend.get_object_meta(
1276 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
1277 2715ade4 Sofia Papagiannaki
                v_container, v_object, 'pithos')
1278 a8326bef Antony Chazapis
        except NotAllowedError:
1279 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1280 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
1281 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Container or object does not exist')
1282 a8326bef Antony Chazapis
        validate_matching_preconditions(request, meta)
1283 2715ade4 Sofia Papagiannaki
1284 4d15c94e Sofia Papagiannaki
    delimiter = request.GET.get('delimiter')
1285 2715ade4 Sofia Papagiannaki
1286 79bb41b7 Antony Chazapis
    version_id = copy_or_move_object(request, v_account, v_container, v_object,
1287 67100dd2 Ilias Tsitsimpis
                                     dest_account, dest_container, dest_name,
1288 67100dd2 Ilias Tsitsimpis
                                     move=True, delimiter=delimiter)
1289 7dd293a0 Antony Chazapis
    response = HttpResponse(status=201)
1290 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1291 7dd293a0 Antony Chazapis
    return response
1292 b956618e Antony Chazapis
1293 2715ade4 Sofia Papagiannaki
1294 b90584d0 Sofia Papagiannaki
@api_method('POST', format_allowed=True, user_required=True, logger=logger,
1295 b90584d0 Sofia Papagiannaki
            lock_container_path=True)
1296 b956618e Antony Chazapis
def object_update(request, v_account, v_container, v_object):
1297 e9285524 Antony Chazapis
    # Normal Response Codes: 202, 204
1298 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1299 3436eeb0 Antony Chazapis
    #                       conflict (409),
1300 b956618e Antony Chazapis
    #                       itemNotFound (404),
1301 297513ba Antony Chazapis
    #                       forbidden (403),
1302 b956618e Antony Chazapis
    #                       badRequest (400)
1303 2715ade4 Sofia Papagiannaki
1304 66ce2ca5 Antony Chazapis
    content_type, meta, permissions, public = get_object_headers(request)
1305 2715ade4 Sofia Papagiannaki
1306 cfe6939d Antony Chazapis
    try:
1307 2715ade4 Sofia Papagiannaki
        prev_meta = request.backend.get_object_meta(
1308 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account,
1309 2715ade4 Sofia Papagiannaki
            v_container, v_object, 'pithos')
1310 cca6c617 Antony Chazapis
    except NotAllowedError:
1311 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
1312 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
1313 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Object does not exist')
1314 2715ade4 Sofia Papagiannaki
1315 a8326bef Antony Chazapis
    # Evaluate conditions.
1316 67100dd2 Ilias Tsitsimpis
    if (request.META.get('HTTP_IF_MATCH')
1317 67100dd2 Ilias Tsitsimpis
            or request.META.get('HTTP_IF_NONE_MATCH')):
1318 a8326bef Antony Chazapis
        validate_matching_preconditions(request, prev_meta)
1319 2715ade4 Sofia Papagiannaki
1320 ac62f6da Antony Chazapis
    replace = True
1321 ac62f6da Antony Chazapis
    if 'update' in request.GET:
1322 ac62f6da Antony Chazapis
        replace = False
1323 2715ade4 Sofia Papagiannaki
1324 ab2e317e Antony Chazapis
    # A Content-Type or X-Source-Object header indicates data updates.
1325 ab2e317e Antony Chazapis
    src_object = request.META.get('HTTP_X_SOURCE_OBJECT')
1326 67100dd2 Ilias Tsitsimpis
    if ((not content_type or content_type != 'application/octet-stream')
1327 67100dd2 Ilias Tsitsimpis
            and not src_object):
1328 77edd23d Antony Chazapis
        response = HttpResponse(status=202)
1329 2715ade4 Sofia Papagiannaki
1330 cca6c617 Antony Chazapis
        # Do permissions first, as it may fail easier.
1331 cca6c617 Antony Chazapis
        if permissions is not None:
1332 ac62f6da Antony Chazapis
            try:
1333 29148653 Sofia Papagiannaki
                request.backend.update_object_permissions(
1334 29148653 Sofia Papagiannaki
                    request.user_uniq, v_account, v_container, v_object,
1335 29148653 Sofia Papagiannaki
                    permissions)
1336 cca6c617 Antony Chazapis
            except NotAllowedError:
1337 bd40abfa Christos Stavrakakis
                raise faults.Forbidden('Not allowed')
1338 7efc9f86 Sofia Papagiannaki
            except ItemNotExists:
1339 bd40abfa Christos Stavrakakis
                raise faults.ItemNotFound('Object does not exist')
1340 ac62f6da Antony Chazapis
            except ValueError:
1341 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Invalid sharing header')
1342 e0f916bb Antony Chazapis
        if public is not None:
1343 e0f916bb Antony Chazapis
            try:
1344 2715ade4 Sofia Papagiannaki
                request.backend.update_object_public(
1345 2715ade4 Sofia Papagiannaki
                    request.user_uniq, v_account,
1346 2715ade4 Sofia Papagiannaki
                    v_container, v_object, public)
1347 e0f916bb Antony Chazapis
            except NotAllowedError:
1348 bd40abfa Christos Stavrakakis
                raise faults.Forbidden('Not allowed')
1349 7efc9f86 Sofia Papagiannaki
            except ItemNotExists:
1350 bd40abfa Christos Stavrakakis
                raise faults.ItemNotFound('Object does not exist')
1351 77edd23d Antony Chazapis
        if meta or replace:
1352 77edd23d Antony Chazapis
            try:
1353 2715ade4 Sofia Papagiannaki
                version_id = request.backend.update_object_meta(
1354 2715ade4 Sofia Papagiannaki
                    request.user_uniq,
1355 2715ade4 Sofia Papagiannaki
                    v_account, v_container, v_object, 'pithos', meta, replace)
1356 77edd23d Antony Chazapis
            except NotAllowedError:
1357 bd40abfa Christos Stavrakakis
                raise faults.Forbidden('Not allowed')
1358 7efc9f86 Sofia Papagiannaki
            except ItemNotExists:
1359 bd40abfa Christos Stavrakakis
                raise faults.ItemNotFound('Object does not exist')
1360 77edd23d Antony Chazapis
            response['X-Object-Version'] = version_id
1361 2715ade4 Sofia Papagiannaki
1362 7dd293a0 Antony Chazapis
        return response
1363 2715ade4 Sofia Papagiannaki
1364 cfe6939d Antony Chazapis
    # Single range update. Range must be in Content-Range.
1365 22dab079 Antony Chazapis
    # Based on: http://code.google.com/p/gears/wiki/ContentRangePostProposal
1366 cfe6939d Antony Chazapis
    # (with the addition that '*' is allowed for the range - will append).
1367 22dab079 Antony Chazapis
    content_range = request.META.get('HTTP_CONTENT_RANGE')
1368 22dab079 Antony Chazapis
    if not content_range:
1369 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Missing Content-Range header')
1370 22dab079 Antony Chazapis
    ranges = get_content_range(request)
1371 22dab079 Antony Chazapis
    if not ranges:
1372 bd40abfa Christos Stavrakakis
        raise faults.RangeNotSatisfiable('Invalid Content-Range header')
1373 2715ade4 Sofia Papagiannaki
1374 cfe6939d Antony Chazapis
    try:
1375 67100dd2 Ilias Tsitsimpis
        size, hashmap = \
1376 29148653 Sofia Papagiannaki
            request.backend.get_object_hashmap(
1377 29148653 Sofia Papagiannaki
                request.user_uniq, v_account, v_container, v_object)
1378 cca6c617 Antony Chazapis
    except NotAllowedError:
1379 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
1380 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
1381 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Object does not exist')
1382 2715ade4 Sofia Papagiannaki
1383 cfe6939d Antony Chazapis
    offset, length, total = ranges
1384 cfe6939d Antony Chazapis
    if offset is None:
1385 cfe6939d Antony Chazapis
        offset = size
1386 cb146cf9 Antony Chazapis
    elif offset > size:
1387 67100dd2 Ilias Tsitsimpis
        raise faults.RangeNotSatisfiable(
1388 67100dd2 Ilias Tsitsimpis
            'Supplied offset is beyond object limits')
1389 ab2e317e Antony Chazapis
    if src_object:
1390 88283e9e Antony Chazapis
        src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
1391 2cd94d81 Antony Chazapis
        if not src_account:
1392 61efb530 Antony Chazapis
            src_account = request.user_uniq
1393 ab2e317e Antony Chazapis
        try:
1394 752a2ddd Sofia Papagiannaki
            src_container, src_name = split_container_object_string(src_object)
1395 752a2ddd Sofia Papagiannaki
        except ValueError:
1396 752a2ddd Sofia Papagiannaki
            raise faults.BadRequest('Invalid source object')
1397 752a2ddd Sofia Papagiannaki
1398 752a2ddd Sofia Papagiannaki
        try:
1399 752a2ddd Sofia Papagiannaki
            src_version = request.META.get('HTTP_X_SOURCE_VERSION')
1400 2715ade4 Sofia Papagiannaki
            src_size, src_hashmap = request.backend.get_object_hashmap(
1401 2715ade4 Sofia Papagiannaki
                request.user_uniq,
1402 2715ade4 Sofia Papagiannaki
                src_account, src_container, src_name, src_version)
1403 ab2e317e Antony Chazapis
        except NotAllowedError:
1404 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1405 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
1406 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Source object does not exist')
1407 2715ade4 Sofia Papagiannaki
1408 ab2e317e Antony Chazapis
        if length is None:
1409 ab2e317e Antony Chazapis
            length = src_size
1410 ab2e317e Antony Chazapis
        elif length > src_size:
1411 67100dd2 Ilias Tsitsimpis
            raise faults.BadRequest(
1412 67100dd2 Ilias Tsitsimpis
                'Object length is smaller than range length')
1413 ab2e317e Antony Chazapis
    else:
1414 ab2e317e Antony Chazapis
        # Require either a Content-Length, or 'chunked' Transfer-Encoding.
1415 ab2e317e Antony Chazapis
        content_length = -1
1416 ab2e317e Antony Chazapis
        if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
1417 ab2e317e Antony Chazapis
            content_length = get_content_length(request)
1418 2715ade4 Sofia Papagiannaki
1419 ab2e317e Antony Chazapis
        if length is None:
1420 ab2e317e Antony Chazapis
            length = content_length
1421 ab2e317e Antony Chazapis
        else:
1422 ab2e317e Antony Chazapis
            if content_length == -1:
1423 ab2e317e Antony Chazapis
                # TODO: Get up to length bytes in chunks.
1424 ab2e317e Antony Chazapis
                length = content_length
1425 ab2e317e Antony Chazapis
            elif length != content_length:
1426 67100dd2 Ilias Tsitsimpis
                raise faults.BadRequest(
1427 67100dd2 Ilias Tsitsimpis
                    'Content length does not match range length')
1428 67100dd2 Ilias Tsitsimpis
    if (total is not None
1429 67100dd2 Ilias Tsitsimpis
            and (total != size or offset >= size
1430 7b40f056 Sofia Papagiannaki
                 or (length > 0 and offset + length > size))):
1431 bd40abfa Christos Stavrakakis
        raise faults.RangeNotSatisfiable(
1432 2715ade4 Sofia Papagiannaki
            'Supplied range will change provided object limits')
1433 2715ade4 Sofia Papagiannaki
1434 1495b972 Antony Chazapis
    dest_bytes = request.META.get('HTTP_X_OBJECT_BYTES')
1435 1495b972 Antony Chazapis
    if dest_bytes is not None:
1436 1495b972 Antony Chazapis
        dest_bytes = get_int_parameter(dest_bytes)
1437 1495b972 Antony Chazapis
        if dest_bytes is None:
1438 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid X-Object-Bytes header')
1439 2715ade4 Sofia Papagiannaki
1440 ab2e317e Antony Chazapis
    if src_object:
1441 39593b2b Giorgos Verigakis
        if offset % request.backend.block_size == 0:
1442 ab2e317e Antony Chazapis
            # Update the hashes only.
1443 ab2e317e Antony Chazapis
            sbi = 0
1444 ab2e317e Antony Chazapis
            while length > 0:
1445 39593b2b Giorgos Verigakis
                bi = int(offset / request.backend.block_size)
1446 39593b2b Giorgos Verigakis
                bl = min(length, request.backend.block_size)
1447 ab2e317e Antony Chazapis
                if bi < len(hashmap):
1448 39593b2b Giorgos Verigakis
                    if bl == request.backend.block_size:
1449 ab2e317e Antony Chazapis
                        hashmap[bi] = src_hashmap[sbi]
1450 ab2e317e Antony Chazapis
                    else:
1451 39593b2b Giorgos Verigakis
                        data = request.backend.get_block(src_hashmap[sbi])
1452 29148653 Sofia Papagiannaki
                        hashmap[bi] = request.backend.update_block(
1453 29148653 Sofia Papagiannaki
                            hashmap[bi], data[:bl], 0)
1454 ab2e317e Antony Chazapis
                else:
1455 ab2e317e Antony Chazapis
                    hashmap.append(src_hashmap[sbi])
1456 ab2e317e Antony Chazapis
                offset += bl
1457 ab2e317e Antony Chazapis
                length -= bl
1458 ab2e317e Antony Chazapis
                sbi += 1
1459 ab2e317e Antony Chazapis
        else:
1460 ab2e317e Antony Chazapis
            data = ''
1461 ab2e317e Antony Chazapis
            sbi = 0
1462 ab2e317e Antony Chazapis
            while length > 0:
1463 f5da7eaa Sofia Papagiannaki
                if sbi < len(src_hashmap):
1464 f5da7eaa Sofia Papagiannaki
                    data += request.backend.get_block(src_hashmap[sbi])
1465 39593b2b Giorgos Verigakis
                if length < request.backend.block_size:
1466 ab2e317e Antony Chazapis
                    data = data[:length]
1467 39593b2b Giorgos Verigakis
                bytes = put_object_block(request, hashmap, data, offset)
1468 ab2e317e Antony Chazapis
                offset += bytes
1469 ab2e317e Antony Chazapis
                data = data[bytes:]
1470 ab2e317e Antony Chazapis
                length -= bytes
1471 ab2e317e Antony Chazapis
                sbi += 1
1472 ab2e317e Antony Chazapis
    else:
1473 ab2e317e Antony Chazapis
        data = ''
1474 39593b2b Giorgos Verigakis
        for d in socket_read_iterator(request, length,
1475 2715ade4 Sofia Papagiannaki
                                      request.backend.block_size):
1476 ab2e317e Antony Chazapis
            # TODO: Raise 408 (Request Timeout) if this takes too long.
1477 67100dd2 Ilias Tsitsimpis
            # TODO: Raise 499 (Client Disconnect) if a length is defined
1478 67100dd2 Ilias Tsitsimpis
            #       and we stop before getting this much data.
1479 ab2e317e Antony Chazapis
            data += d
1480 39593b2b Giorgos Verigakis
            bytes = put_object_block(request, hashmap, data, offset)
1481 ab2e317e Antony Chazapis
            offset += bytes
1482 ab2e317e Antony Chazapis
            data = data[bytes:]
1483 ab2e317e Antony Chazapis
        if len(data) > 0:
1484 80d668be Sofia Papagiannaki
            bytes = put_object_block(request, hashmap, data, offset)
1485 80d668be Sofia Papagiannaki
            offset += bytes
1486 2715ade4 Sofia Papagiannaki
1487 cfe6939d Antony Chazapis
    if offset > size:
1488 cfe6939d Antony Chazapis
        size = offset
1489 1495b972 Antony Chazapis
    if dest_bytes is not None and dest_bytes < size:
1490 1495b972 Antony Chazapis
        size = dest_bytes
1491 39593b2b Giorgos Verigakis
        hashmap = hashmap[:(int((size - 1) / request.backend.block_size) + 1)]
1492 2715ade4 Sofia Papagiannaki
    checksum = hashmap_md5(
1493 2715ade4 Sofia Papagiannaki
        request.backend, hashmap, size) if UPDATE_MD5 else ''
1494 cfe6939d Antony Chazapis
    try:
1495 133e3fcf Sofia Papagiannaki
        version_id, merkle = request.backend.update_object_hashmap(
1496 133e3fcf Sofia Papagiannaki
            request.user_uniq, v_account, v_container, v_object, size,
1497 133e3fcf Sofia Papagiannaki
            prev_meta['type'], hashmap, checksum, 'pithos', meta, replace,
1498 133e3fcf Sofia Papagiannaki
            permissions
1499 133e3fcf Sofia Papagiannaki
        )
1500 cca6c617 Antony Chazapis
    except NotAllowedError:
1501 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
1502 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
1503 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Container does not exist')
1504 3436eeb0 Antony Chazapis
    except ValueError:
1505 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid sharing header')
1506 ccfd4e44 Sofia Papagiannaki
    except QuotaError, e:
1507 bd40abfa Christos Stavrakakis
        raise faults.RequestEntityTooLarge('Quota error: %s' % e)
1508 e0f916bb Antony Chazapis
    if public is not None:
1509 e0f916bb Antony Chazapis
        try:
1510 61efb530 Antony Chazapis
            request.backend.update_object_public(request.user_uniq, v_account,
1511 2715ade4 Sofia Papagiannaki
                                                 v_container, v_object, public)
1512 e0f916bb Antony Chazapis
        except NotAllowedError:
1513 bd40abfa Christos Stavrakakis
            raise faults.Forbidden('Not allowed')
1514 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
1515 bd40abfa Christos Stavrakakis
            raise faults.ItemNotFound('Object does not exist')
1516 2715ade4 Sofia Papagiannaki
1517 e9285524 Antony Chazapis
    response = HttpResponse(status=204)
1518 133e3fcf Sofia Papagiannaki
    response['ETag'] = merkle if not UPDATE_MD5 else checksum
1519 7dd293a0 Antony Chazapis
    response['X-Object-Version'] = version_id
1520 e9285524 Antony Chazapis
    return response
1521 b956618e Antony Chazapis
1522 2715ade4 Sofia Papagiannaki
1523 b90584d0 Sofia Papagiannaki
@api_method('DELETE', user_required=True, logger=logger,
1524 b90584d0 Sofia Papagiannaki
            lock_container_path=True)
1525 b956618e Antony Chazapis
def object_delete(request, v_account, v_container, v_object):
1526 b956618e Antony Chazapis
    # Normal Response Codes: 204
1527 08de868d Antony Chazapis
    # Error Response Codes: internalServerError (500),
1528 b956618e Antony Chazapis
    #                       itemNotFound (404),
1529 297513ba Antony Chazapis
    #                       forbidden (403),
1530 b956618e Antony Chazapis
    #                       badRequest (400)
1531 dfa2d4ba Sofia Papagiannaki
    #                       requestentitytoolarge (413)
1532 2715ade4 Sofia Papagiannaki
1533 bbd20b55 Antony Chazapis
    until = get_int_parameter(request.GET.get('until'))
1534 4d15c94e Sofia Papagiannaki
    delimiter = request.GET.get('delimiter')
1535 2715ade4 Sofia Papagiannaki
1536 b956618e Antony Chazapis
    try:
1537 2715ade4 Sofia Papagiannaki
        request.backend.delete_object(
1538 2715ade4 Sofia Papagiannaki
            request.user_uniq, v_account, v_container,
1539 2715ade4 Sofia Papagiannaki
            v_object, until, delimiter=delimiter)
1540 cca6c617 Antony Chazapis
    except NotAllowedError:
1541 bd40abfa Christos Stavrakakis
        raise faults.Forbidden('Not allowed')
1542 7efc9f86 Sofia Papagiannaki
    except ItemNotExists:
1543 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Object does not exist')
1544 dfa2d4ba Sofia Papagiannaki
    except QuotaError, e:
1545 bd40abfa Christos Stavrakakis
        raise faults.RequestEntityTooLarge('Quota error: %s' % e)
1546 b956618e Antony Chazapis
    return HttpResponse(status=204)