Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (58.3 kB)

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