Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / util.py @ f4fbb0fa

History | View | Annotate | Download (36.6 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 b956618e Antony Chazapis
from functools import wraps
35 b956618e Antony Chazapis
from time import time
36 b956618e Antony Chazapis
from traceback import format_exc
37 b956618e Antony Chazapis
from wsgiref.handlers import format_date_time
38 0d4ea090 Antony Chazapis
from binascii import hexlify, unhexlify
39 e11087c2 Antony Chazapis
from datetime import datetime, tzinfo, timedelta
40 9fefc052 Antony Chazapis
from urllib import quote, unquote
41 b956618e Antony Chazapis
42 b956618e Antony Chazapis
from django.conf import settings
43 b956618e Antony Chazapis
from django.http import HttpResponse
44 6b6b6c1e Antony Chazapis
from django.template.loader import render_to_string
45 1993fea9 Antony Chazapis
from django.utils import simplejson as json
46 22dab079 Antony Chazapis
from django.utils.http import http_date, parse_etags
47 fb064032 Antony Chazapis
from django.utils.encoding import smart_unicode, smart_str
48 817890f2 Antony Chazapis
from django.core.files.uploadhandler import FileUploadHandler
49 817890f2 Antony Chazapis
from django.core.files.uploadedfile import UploadedFile
50 b956618e Antony Chazapis
51 6e147ecc Antony Chazapis
from synnefo.lib.parsedate import parse_http_date_safe, parse_http_date
52 dfdf4802 Sofia Papagiannaki
from synnefo.lib.astakos import get_user
53 5a96180b Antony Chazapis
54 2715ade4 Sofia Papagiannaki
from pithos.api.faults import (
55 2715ade4 Sofia Papagiannaki
    Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound,
56 2715ade4 Sofia Papagiannaki
    Conflict, LengthRequired, PreconditionFailed, RequestEntityTooLarge,
57 2715ade4 Sofia Papagiannaki
    RangeNotSatisfiable, InternalServerError, NotImplemented)
58 bb4eafc6 Antony Chazapis
from pithos.api.short_url import encode_url
59 a7dff008 Antony Chazapis
from pithos.api.settings import (BACKEND_DB_MODULE, BACKEND_DB_CONNECTION,
60 2715ade4 Sofia Papagiannaki
                                 BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH,
61 2715ade4 Sofia Papagiannaki
                                 BACKEND_BLOCK_UMASK,
62 f4fbb0fa Sofia Papagiannaki
                                 BACKEND_QUEUE_MODULE, BACKEND_QUEUE_HOSTS,
63 f4fbb0fa Sofia Papagiannaki
                                 BACKEND_QUEUE_EXCHANGE,
64 2715ade4 Sofia Papagiannaki
                                 BACKEND_QUOTA, BACKEND_VERSIONING,
65 2715ade4 Sofia Papagiannaki
                                 AUTHENTICATION_URL, AUTHENTICATION_USERS,
66 2715ade4 Sofia Papagiannaki
                                 SERVICE_TOKEN, COOKIE_NAME)
67 dfdf4802 Sofia Papagiannaki
68 39593b2b Giorgos Verigakis
from pithos.backends import connect_backend
69 7efc9f86 Sofia Papagiannaki
from pithos.backends.base import NotAllowedError, QuotaError, ItemNotExists, VersionNotExists
70 b956618e Antony Chazapis
71 b956618e Antony Chazapis
import logging
72 22dab079 Antony Chazapis
import re
73 cbfb6636 Sofia Papagiannaki
import hashlib
74 7bef5750 Antony Chazapis
import uuid
75 c48acbfd Sofia Papagiannaki
import decimal
76 8c793655 Antony Chazapis
77 b956618e Antony Chazapis
78 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
79 b956618e Antony Chazapis
80 b956618e Antony Chazapis
81 e11087c2 Antony Chazapis
class UTC(tzinfo):
82 2715ade4 Sofia Papagiannaki
    def utcoffset(self, dt):
83 2715ade4 Sofia Papagiannaki
        return timedelta(0)
84 e11087c2 Antony Chazapis
85 2715ade4 Sofia Papagiannaki
    def tzname(self, dt):
86 2715ade4 Sofia Papagiannaki
        return 'UTC'
87 2715ade4 Sofia Papagiannaki
88 2715ade4 Sofia Papagiannaki
    def dst(self, dt):
89 2715ade4 Sofia Papagiannaki
        return timedelta(0)
90 e11087c2 Antony Chazapis
91 e11087c2 Antony Chazapis
92 c48acbfd Sofia Papagiannaki
def json_encode_decimal(obj):
93 c48acbfd Sofia Papagiannaki
    if isinstance(obj, decimal.Decimal):
94 c48acbfd Sofia Papagiannaki
        return str(obj)
95 c48acbfd Sofia Papagiannaki
    raise TypeError(repr(obj) + " is not JSON serializable")
96 c48acbfd Sofia Papagiannaki
97 2715ade4 Sofia Papagiannaki
98 e11087c2 Antony Chazapis
def isoformat(d):
99 2715ade4 Sofia Papagiannaki
    """Return an ISO8601 date string that includes a timezone."""
100 2715ade4 Sofia Papagiannaki
101 2715ade4 Sofia Papagiannaki
    return d.replace(tzinfo=UTC()).isoformat()
102 e11087c2 Antony Chazapis
103 e11087c2 Antony Chazapis
104 804e8fe7 Antony Chazapis
def rename_meta_key(d, old, new):
105 804e8fe7 Antony Chazapis
    if old not in d:
106 804e8fe7 Antony Chazapis
        return
107 804e8fe7 Antony Chazapis
    d[new] = d[old]
108 804e8fe7 Antony Chazapis
    del(d[old])
109 804e8fe7 Antony Chazapis
110 2715ade4 Sofia Papagiannaki
111 02c0c3fa Antony Chazapis
def printable_header_dict(d):
112 b956618e Antony Chazapis
    """Format a meta dictionary for printing out json/xml.
113 2715ade4 Sofia Papagiannaki

114 804e8fe7 Antony Chazapis
    Convert all keys to lower case and replace dashes with underscores.
115 804e8fe7 Antony Chazapis
    Format 'last_modified' timestamp.
116 b956618e Antony Chazapis
    """
117 2715ade4 Sofia Papagiannaki
118 371d907a Antony Chazapis
    if 'last_modified' in d and d['last_modified']:
119 2715ade4 Sofia Papagiannaki
        d['last_modified'] = isoformat(
120 2715ade4 Sofia Papagiannaki
            datetime.fromtimestamp(d['last_modified']))
121 b956618e Antony Chazapis
    return dict([(k.lower().replace('-', '_'), v) for k, v in d.iteritems()])
122 b956618e Antony Chazapis
123 2715ade4 Sofia Papagiannaki
124 02c0c3fa Antony Chazapis
def format_header_key(k):
125 58a6c894 Antony Chazapis
    """Convert underscores to dashes and capitalize intra-dash strings."""
126 b956618e Antony Chazapis
    return '-'.join([x.capitalize() for x in k.replace('_', '-').split('-')])
127 b956618e Antony Chazapis
128 2715ade4 Sofia Papagiannaki
129 02c0c3fa Antony Chazapis
def get_header_prefix(request, prefix):
130 02c0c3fa Antony Chazapis
    """Get all prefix-* request headers in a dict. Reformat keys with format_header_key()."""
131 2715ade4 Sofia Papagiannaki
132 b956618e Antony Chazapis
    prefix = 'HTTP_' + prefix.upper().replace('-', '_')
133 3ab38c43 Antony Chazapis
    # TODO: Document or remove '~' replacing.
134 3ab38c43 Antony Chazapis
    return dict([(format_header_key(k[5:]), v.replace('~', '')) for k, v in request.META.iteritems() if k.startswith(prefix) and len(k) > len(prefix)])
135 02c0c3fa Antony Chazapis
136 2715ade4 Sofia Papagiannaki
137 62bf8157 Antony Chazapis
def check_meta_headers(meta):
138 62bf8157 Antony Chazapis
    if len(meta) > 90:
139 62bf8157 Antony Chazapis
        raise BadRequest('Too many headers.')
140 62bf8157 Antony Chazapis
    for k, v in meta.iteritems():
141 62bf8157 Antony Chazapis
        if len(k) > 128:
142 62bf8157 Antony Chazapis
            raise BadRequest('Header name too large.')
143 62bf8157 Antony Chazapis
        if len(v) > 256:
144 62bf8157 Antony Chazapis
            raise BadRequest('Header value too large.')
145 62bf8157 Antony Chazapis
146 2715ade4 Sofia Papagiannaki
147 02c0c3fa Antony Chazapis
def get_account_headers(request):
148 02c0c3fa Antony Chazapis
    meta = get_header_prefix(request, 'X-Account-Meta-')
149 62bf8157 Antony Chazapis
    check_meta_headers(meta)
150 02c0c3fa Antony Chazapis
    groups = {}
151 02c0c3fa Antony Chazapis
    for k, v in get_header_prefix(request, 'X-Account-Group-').iteritems():
152 02c0c3fa Antony Chazapis
        n = k[16:].lower()
153 02c0c3fa Antony Chazapis
        if '-' in n or '_' in n:
154 02c0c3fa Antony Chazapis
            raise BadRequest('Bad characters in group name')
155 02c0c3fa Antony Chazapis
        groups[n] = v.replace(' ', '').split(',')
156 40d6b76d Antony Chazapis
        while '' in groups[n]:
157 02c0c3fa Antony Chazapis
            groups[n].remove('')
158 02c0c3fa Antony Chazapis
    return meta, groups
159 02c0c3fa Antony Chazapis
160 2715ade4 Sofia Papagiannaki
161 647a5f48 Antony Chazapis
def put_account_headers(response, meta, groups, policy):
162 f6c97079 Antony Chazapis
    if 'count' in meta:
163 f6c97079 Antony Chazapis
        response['X-Account-Container-Count'] = meta['count']
164 f6c97079 Antony Chazapis
    if 'bytes' in meta:
165 f6c97079 Antony Chazapis
        response['X-Account-Bytes-Used'] = meta['bytes']
166 d065f612 Antony Chazapis
    response['Last-Modified'] = http_date(int(meta['modified']))
167 b956618e Antony Chazapis
    for k in [x for x in meta.keys() if x.startswith('X-Account-Meta-')]:
168 2715ade4 Sofia Papagiannaki
        response[smart_str(
169 2715ade4 Sofia Papagiannaki
            k, strings_only=True)] = smart_str(meta[k], strings_only=True)
170 83dd59c5 Antony Chazapis
    if 'until_timestamp' in meta:
171 2715ade4 Sofia Papagiannaki
        response['X-Account-Until-Timestamp'] = http_date(
172 2715ade4 Sofia Papagiannaki
            int(meta['until_timestamp']))
173 02c0c3fa Antony Chazapis
    for k, v in groups.iteritems():
174 e7b51248 Sofia Papagiannaki
        k = smart_str(k, strings_only=True)
175 e7b51248 Sofia Papagiannaki
        k = format_header_key('X-Account-Group-' + k)
176 e7b51248 Sofia Papagiannaki
        v = smart_str(','.join(v), strings_only=True)
177 e7b51248 Sofia Papagiannaki
        response[k] = v
178 647a5f48 Antony Chazapis
    for k, v in policy.iteritems():
179 647a5f48 Antony Chazapis
        response[smart_str(format_header_key('X-Account-Policy-' + k), strings_only=True)] = smart_str(v, strings_only=True)
180 647a5f48 Antony Chazapis
181 2715ade4 Sofia Papagiannaki
182 02c0c3fa Antony Chazapis
def get_container_headers(request):
183 02c0c3fa Antony Chazapis
    meta = get_header_prefix(request, 'X-Container-Meta-')
184 62bf8157 Antony Chazapis
    check_meta_headers(meta)
185 3ab38c43 Antony Chazapis
    policy = dict([(k[19:].lower(), v.replace(' ', '')) for k, v in get_header_prefix(request, 'X-Container-Policy-').iteritems()])
186 3ab38c43 Antony Chazapis
    return meta, policy
187 b956618e Antony Chazapis
188 2715ade4 Sofia Papagiannaki
189 39593b2b Giorgos Verigakis
def put_container_headers(request, response, meta, policy):
190 f6c97079 Antony Chazapis
    if 'count' in meta:
191 f6c97079 Antony Chazapis
        response['X-Container-Object-Count'] = meta['count']
192 f6c97079 Antony Chazapis
    if 'bytes' in meta:
193 f6c97079 Antony Chazapis
        response['X-Container-Bytes-Used'] = meta['bytes']
194 58a6c894 Antony Chazapis
    response['Last-Modified'] = http_date(int(meta['modified']))
195 b956618e Antony Chazapis
    for k in [x for x in meta.keys() if x.startswith('X-Container-Meta-')]:
196 2715ade4 Sofia Papagiannaki
        response[smart_str(
197 2715ade4 Sofia Papagiannaki
            k, strings_only=True)] = smart_str(meta[k], strings_only=True)
198 2715ade4 Sofia Papagiannaki
    l = [smart_str(x, strings_only=True) for x in meta['object_meta']
199 2715ade4 Sofia Papagiannaki
         if x.startswith('X-Object-Meta-')]
200 e7b51248 Sofia Papagiannaki
    response['X-Container-Object-Meta'] = ','.join([x[14:] for x in l])
201 39593b2b Giorgos Verigakis
    response['X-Container-Block-Size'] = request.backend.block_size
202 39593b2b Giorgos Verigakis
    response['X-Container-Block-Hash'] = request.backend.hash_algorithm
203 83dd59c5 Antony Chazapis
    if 'until_timestamp' in meta:
204 2715ade4 Sofia Papagiannaki
        response['X-Container-Until-Timestamp'] = http_date(
205 2715ade4 Sofia Papagiannaki
            int(meta['until_timestamp']))
206 3ab38c43 Antony Chazapis
    for k, v in policy.iteritems():
207 e7b51248 Sofia Papagiannaki
        response[smart_str(format_header_key('X-Container-Policy-' + k), strings_only=True)] = smart_str(v, strings_only=True)
208 b956618e Antony Chazapis
209 2715ade4 Sofia Papagiannaki
210 02c0c3fa Antony Chazapis
def get_object_headers(request):
211 66ce2ca5 Antony Chazapis
    content_type = request.META.get('CONTENT_TYPE', None)
212 02c0c3fa Antony Chazapis
    meta = get_header_prefix(request, 'X-Object-Meta-')
213 62bf8157 Antony Chazapis
    check_meta_headers(meta)
214 b956618e Antony Chazapis
    if request.META.get('HTTP_CONTENT_ENCODING'):
215 b956618e Antony Chazapis
        meta['Content-Encoding'] = request.META['HTTP_CONTENT_ENCODING']
216 22dab079 Antony Chazapis
    if request.META.get('HTTP_CONTENT_DISPOSITION'):
217 22dab079 Antony Chazapis
        meta['Content-Disposition'] = request.META['HTTP_CONTENT_DISPOSITION']
218 b956618e Antony Chazapis
    if request.META.get('HTTP_X_OBJECT_MANIFEST'):
219 b956618e Antony Chazapis
        meta['X-Object-Manifest'] = request.META['HTTP_X_OBJECT_MANIFEST']
220 66ce2ca5 Antony Chazapis
    return content_type, meta, get_sharing(request), get_public(request)
221 b956618e Antony Chazapis
222 2715ade4 Sofia Papagiannaki
223 3ab38c43 Antony Chazapis
def put_object_headers(response, meta, restricted=False):
224 33b4e4a6 Antony Chazapis
    response['ETag'] = meta['checksum']
225 b956618e Antony Chazapis
    response['Content-Length'] = meta['bytes']
226 66ce2ca5 Antony Chazapis
    response['Content-Type'] = meta.get('type', 'application/octet-stream')
227 b956618e Antony Chazapis
    response['Last-Modified'] = http_date(int(meta['modified']))
228 3ab38c43 Antony Chazapis
    if not restricted:
229 4a1c29ea Antony Chazapis
        response['X-Object-Hash'] = meta['hash']
230 37bee317 Antony Chazapis
        response['X-Object-UUID'] = meta['uuid']
231 2715ade4 Sofia Papagiannaki
        response['X-Object-Modified-By'] = smart_str(
232 2715ade4 Sofia Papagiannaki
            meta['modified_by'], strings_only=True)
233 7bef5750 Antony Chazapis
        response['X-Object-Version'] = meta['version']
234 2715ade4 Sofia Papagiannaki
        response['X-Object-Version-Timestamp'] = http_date(
235 2715ade4 Sofia Papagiannaki
            int(meta['version_timestamp']))
236 7bef5750 Antony Chazapis
        for k in [x for x in meta.keys() if x.startswith('X-Object-Meta-')]:
237 2715ade4 Sofia Papagiannaki
            response[smart_str(
238 2715ade4 Sofia Papagiannaki
                k, strings_only=True)] = smart_str(meta[k], strings_only=True)
239 2715ade4 Sofia Papagiannaki
        for k in (
240 2715ade4 Sofia Papagiannaki
            'Content-Encoding', 'Content-Disposition', 'X-Object-Manifest',
241 2715ade4 Sofia Papagiannaki
            'X-Object-Sharing', 'X-Object-Shared-By', 'X-Object-Allowed-To',
242 2715ade4 Sofia Papagiannaki
                'X-Object-Public'):
243 7bef5750 Antony Chazapis
            if k in meta:
244 e7b51248 Sofia Papagiannaki
                response[k] = smart_str(meta[k], strings_only=True)
245 7bef5750 Antony Chazapis
    else:
246 c9af0703 Antony Chazapis
        for k in ('Content-Encoding', 'Content-Disposition'):
247 7bef5750 Antony Chazapis
            if k in meta:
248 e8d003e8 Antony Chazapis
                response[k] = smart_str(meta[k], strings_only=True)
249 b956618e Antony Chazapis
250 2715ade4 Sofia Papagiannaki
251 8cb45c13 Antony Chazapis
def update_manifest_meta(request, v_account, meta):
252 8cb45c13 Antony Chazapis
    """Update metadata if the object has an X-Object-Manifest."""
253 2715ade4 Sofia Papagiannaki
254 8cb45c13 Antony Chazapis
    if 'X-Object-Manifest' in meta:
255 4a1c29ea Antony Chazapis
        etag = ''
256 8cb45c13 Antony Chazapis
        bytes = 0
257 8cb45c13 Antony Chazapis
        try:
258 2715ade4 Sofia Papagiannaki
            src_container, src_name = split_container_object_string(
259 2715ade4 Sofia Papagiannaki
                '/' + meta['X-Object-Manifest'])
260 2715ade4 Sofia Papagiannaki
            objects = request.backend.list_objects(
261 2715ade4 Sofia Papagiannaki
                request.user_uniq, v_account,
262 2715ade4 Sofia Papagiannaki
                src_container, prefix=src_name, virtual=False)
263 8cb45c13 Antony Chazapis
            for x in objects:
264 61efb530 Antony Chazapis
                src_meta = request.backend.get_object_meta(request.user_uniq,
265 2715ade4 Sofia Papagiannaki
                                                           v_account, src_container, x[0], 'pithos', x[1])
266 33b4e4a6 Antony Chazapis
                etag += src_meta['checksum']
267 8cb45c13 Antony Chazapis
                bytes += src_meta['bytes']
268 8cb45c13 Antony Chazapis
        except:
269 8cb45c13 Antony Chazapis
            # Ignore errors.
270 8cb45c13 Antony Chazapis
            return
271 8cb45c13 Antony Chazapis
        meta['bytes'] = bytes
272 8cb45c13 Antony Chazapis
        md5 = hashlib.md5()
273 4a1c29ea Antony Chazapis
        md5.update(etag)
274 33b4e4a6 Antony Chazapis
        meta['checksum'] = md5.hexdigest().lower()
275 8cb45c13 Antony Chazapis
276 2715ade4 Sofia Papagiannaki
277 067cf1fc Antony Chazapis
def update_sharing_meta(request, permissions, v_account, v_container, v_object, meta):
278 cca6c617 Antony Chazapis
    if permissions is None:
279 cca6c617 Antony Chazapis
        return
280 067cf1fc Antony Chazapis
    allowed, perm_path, perms = permissions
281 cca6c617 Antony Chazapis
    if len(perms) == 0:
282 cca6c617 Antony Chazapis
        return
283 3436eeb0 Antony Chazapis
    ret = []
284 cca6c617 Antony Chazapis
    r = ','.join(perms.get('read', []))
285 3436eeb0 Antony Chazapis
    if r:
286 3436eeb0 Antony Chazapis
        ret.append('read=' + r)
287 cca6c617 Antony Chazapis
    w = ','.join(perms.get('write', []))
288 3436eeb0 Antony Chazapis
    if w:
289 3436eeb0 Antony Chazapis
        ret.append('write=' + w)
290 cca6c617 Antony Chazapis
    meta['X-Object-Sharing'] = '; '.join(ret)
291 cca6c617 Antony Chazapis
    if '/'.join((v_account, v_container, v_object)) != perm_path:
292 cca6c617 Antony Chazapis
        meta['X-Object-Shared-By'] = perm_path
293 61efb530 Antony Chazapis
    if request.user_uniq != v_account:
294 067cf1fc Antony Chazapis
        meta['X-Object-Allowed-To'] = allowed
295 3436eeb0 Antony Chazapis
296 2715ade4 Sofia Papagiannaki
297 e0f916bb Antony Chazapis
def update_public_meta(public, meta):
298 e0f916bb Antony Chazapis
    if not public:
299 e0f916bb Antony Chazapis
        return
300 bb4eafc6 Antony Chazapis
    meta['X-Object-Public'] = '/public/' + encode_url(public)
301 e0f916bb Antony Chazapis
302 2715ade4 Sofia Papagiannaki
303 b956618e Antony Chazapis
def validate_modification_preconditions(request, meta):
304 58a6c894 Antony Chazapis
    """Check that the modified timestamp conforms with the preconditions set."""
305 2715ade4 Sofia Papagiannaki
306 22dab079 Antony Chazapis
    if 'modified' not in meta:
307 2715ade4 Sofia Papagiannaki
        return  # TODO: Always return?
308 2715ade4 Sofia Papagiannaki
309 b956618e Antony Chazapis
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
310 b956618e Antony Chazapis
    if if_modified_since is not None:
311 b956618e Antony Chazapis
        if_modified_since = parse_http_date_safe(if_modified_since)
312 22dab079 Antony Chazapis
    if if_modified_since is not None and int(meta['modified']) <= if_modified_since:
313 3e5d2f38 Antony Chazapis
        raise NotModified('Resource has not been modified')
314 2715ade4 Sofia Papagiannaki
315 b956618e Antony Chazapis
    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
316 b956618e Antony Chazapis
    if if_unmodified_since is not None:
317 b956618e Antony Chazapis
        if_unmodified_since = parse_http_date_safe(if_unmodified_since)
318 22dab079 Antony Chazapis
    if if_unmodified_since is not None and int(meta['modified']) > if_unmodified_since:
319 3e5d2f38 Antony Chazapis
        raise PreconditionFailed('Resource has been modified')
320 b956618e Antony Chazapis
321 2715ade4 Sofia Papagiannaki
322 22dab079 Antony Chazapis
def validate_matching_preconditions(request, meta):
323 58a6c894 Antony Chazapis
    """Check that the ETag conforms with the preconditions set."""
324 2715ade4 Sofia Papagiannaki
325 33b4e4a6 Antony Chazapis
    etag = meta['checksum']
326 33b4e4a6 Antony Chazapis
    if not etag:
327 33b4e4a6 Antony Chazapis
        etag = None
328 2715ade4 Sofia Papagiannaki
329 22dab079 Antony Chazapis
    if_match = request.META.get('HTTP_IF_MATCH')
330 a8326bef Antony Chazapis
    if if_match is not None:
331 4a1c29ea Antony Chazapis
        if etag is None:
332 a8326bef Antony Chazapis
            raise PreconditionFailed('Resource does not exist')
333 4a1c29ea Antony Chazapis
        if if_match != '*' and etag not in [x.lower() for x in parse_etags(if_match)]:
334 a8326bef Antony Chazapis
            raise PreconditionFailed('Resource ETag does not match')
335 2715ade4 Sofia Papagiannaki
336 22dab079 Antony Chazapis
    if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
337 22dab079 Antony Chazapis
    if if_none_match is not None:
338 a8326bef Antony Chazapis
        # TODO: If this passes, must ignore If-Modified-Since header.
339 4a1c29ea Antony Chazapis
        if etag is not None:
340 4a1c29ea Antony Chazapis
            if if_none_match == '*' or etag in [x.lower() for x in parse_etags(if_none_match)]:
341 a8326bef Antony Chazapis
                # TODO: Continue if an If-Modified-Since header is present.
342 a8326bef Antony Chazapis
                if request.method in ('HEAD', 'GET'):
343 a8326bef Antony Chazapis
                    raise NotModified('Resource ETag matches')
344 a8326bef Antony Chazapis
                raise PreconditionFailed('Resource exists or ETag matches')
345 22dab079 Antony Chazapis
346 2715ade4 Sofia Papagiannaki
347 83dd59c5 Antony Chazapis
def split_container_object_string(s):
348 6d817842 Antony Chazapis
    if not len(s) > 0 or s[0] != '/':
349 6d817842 Antony Chazapis
        raise ValueError
350 6d817842 Antony Chazapis
    s = s[1:]
351 8cb45c13 Antony Chazapis
    pos = s.find('/')
352 22d7b01e Antony Chazapis
    if pos == -1 or pos == len(s) - 1:
353 83dd59c5 Antony Chazapis
        raise ValueError
354 8cb45c13 Antony Chazapis
    return s[:pos], s[(pos + 1):]
355 83dd59c5 Antony Chazapis
356 2715ade4 Sofia Papagiannaki
357 4d15c94e Sofia Papagiannaki
def copy_or_move_object(request, src_account, src_container, src_name, dest_account, dest_container, dest_name, move=False, delimiter=None):
358 58a6c894 Antony Chazapis
    """Copy or move an object."""
359 2715ade4 Sofia Papagiannaki
360 53cff70c Antony Chazapis
    if 'ignore_content_type' in request.GET and 'CONTENT_TYPE' in request.META:
361 53cff70c Antony Chazapis
        del(request.META['CONTENT_TYPE'])
362 66ce2ca5 Antony Chazapis
    content_type, meta, permissions, public = get_object_headers(request)
363 79bb41b7 Antony Chazapis
    src_version = request.META.get('HTTP_X_SOURCE_VERSION')
364 b956618e Antony Chazapis
    try:
365 b956618e Antony Chazapis
        if move:
366 2715ade4 Sofia Papagiannaki
            version_id = request.backend.move_object(
367 2715ade4 Sofia Papagiannaki
                request.user_uniq, src_account, src_container, src_name,
368 2715ade4 Sofia Papagiannaki
                dest_account, dest_container, dest_name,
369 2715ade4 Sofia Papagiannaki
                content_type, 'pithos', meta, False, permissions, delimiter)
370 b956618e Antony Chazapis
        else:
371 2715ade4 Sofia Papagiannaki
            version_id = request.backend.copy_object(
372 2715ade4 Sofia Papagiannaki
                request.user_uniq, src_account, src_container, src_name,
373 2715ade4 Sofia Papagiannaki
                dest_account, dest_container, dest_name,
374 2715ade4 Sofia Papagiannaki
                content_type, 'pithos', meta, False, permissions, src_version, delimiter)
375 cca6c617 Antony Chazapis
    except NotAllowedError:
376 297513ba Antony Chazapis
        raise Forbidden('Not allowed')
377 7efc9f86 Sofia Papagiannaki
    except (ItemNotExists, VersionNotExists):
378 b956618e Antony Chazapis
        raise ItemNotFound('Container or object does not exist')
379 3436eeb0 Antony Chazapis
    except ValueError:
380 3436eeb0 Antony Chazapis
        raise BadRequest('Invalid sharing header')
381 5df6c6d1 Antony Chazapis
    except QuotaError:
382 5df6c6d1 Antony Chazapis
        raise RequestEntityTooLarge('Quota exceeded')
383 e0f916bb Antony Chazapis
    if public is not None:
384 e0f916bb Antony Chazapis
        try:
385 61efb530 Antony Chazapis
            request.backend.update_object_public(request.user_uniq, dest_account, dest_container, dest_name, public)
386 e0f916bb Antony Chazapis
        except NotAllowedError:
387 297513ba Antony Chazapis
            raise Forbidden('Not allowed')
388 7efc9f86 Sofia Papagiannaki
        except ItemNotExists:
389 e0f916bb Antony Chazapis
            raise ItemNotFound('Object does not exist')
390 7dd293a0 Antony Chazapis
    return version_id
391 b956618e Antony Chazapis
392 2715ade4 Sofia Papagiannaki
393 1495b972 Antony Chazapis
def get_int_parameter(p):
394 83dd59c5 Antony Chazapis
    if p is not None:
395 58a6c894 Antony Chazapis
        try:
396 83dd59c5 Antony Chazapis
            p = int(p)
397 58a6c894 Antony Chazapis
        except ValueError:
398 58a6c894 Antony Chazapis
            return None
399 83dd59c5 Antony Chazapis
        if p < 0:
400 58a6c894 Antony Chazapis
            return None
401 83dd59c5 Antony Chazapis
    return p
402 58a6c894 Antony Chazapis
403 2715ade4 Sofia Papagiannaki
404 22dab079 Antony Chazapis
def get_content_length(request):
405 1495b972 Antony Chazapis
    content_length = get_int_parameter(request.META.get('CONTENT_LENGTH'))
406 1495b972 Antony Chazapis
    if content_length is None:
407 1495b972 Antony Chazapis
        raise LengthRequired('Missing or invalid Content-Length header')
408 22dab079 Antony Chazapis
    return content_length
409 22dab079 Antony Chazapis
410 2715ade4 Sofia Papagiannaki
411 22dab079 Antony Chazapis
def get_range(request, size):
412 58a6c894 Antony Chazapis
    """Parse a Range header from the request.
413 2715ade4 Sofia Papagiannaki

414 22dab079 Antony Chazapis
    Either returns None, when the header is not existent or should be ignored,
415 22dab079 Antony Chazapis
    or a list of (offset, length) tuples - should be further checked.
416 b956618e Antony Chazapis
    """
417 2715ade4 Sofia Papagiannaki
418 22dab079 Antony Chazapis
    ranges = request.META.get('HTTP_RANGE', '').replace(' ', '')
419 22dab079 Antony Chazapis
    if not ranges.startswith('bytes='):
420 b956618e Antony Chazapis
        return None
421 2715ade4 Sofia Papagiannaki
422 22dab079 Antony Chazapis
    ret = []
423 22dab079 Antony Chazapis
    for r in (x.strip() for x in ranges[6:].split(',')):
424 22dab079 Antony Chazapis
        p = re.compile('^(?P<offset>\d*)-(?P<upto>\d*)$')
425 22dab079 Antony Chazapis
        m = p.match(r)
426 22dab079 Antony Chazapis
        if not m:
427 22dab079 Antony Chazapis
            return None
428 22dab079 Antony Chazapis
        offset = m.group('offset')
429 22dab079 Antony Chazapis
        upto = m.group('upto')
430 22dab079 Antony Chazapis
        if offset == '' and upto == '':
431 b956618e Antony Chazapis
            return None
432 2715ade4 Sofia Papagiannaki
433 22dab079 Antony Chazapis
        if offset != '':
434 22dab079 Antony Chazapis
            offset = int(offset)
435 22dab079 Antony Chazapis
            if upto != '':
436 b956618e Antony Chazapis
                upto = int(upto)
437 22dab079 Antony Chazapis
                if offset > upto:
438 22dab079 Antony Chazapis
                    return None
439 22dab079 Antony Chazapis
                ret.append((offset, upto - offset + 1))
440 22dab079 Antony Chazapis
            else:
441 22dab079 Antony Chazapis
                ret.append((offset, size - offset))
442 b956618e Antony Chazapis
        else:
443 22dab079 Antony Chazapis
            length = int(upto)
444 22dab079 Antony Chazapis
            ret.append((size - length, length))
445 2715ade4 Sofia Papagiannaki
446 22dab079 Antony Chazapis
    return ret
447 22dab079 Antony Chazapis
448 2715ade4 Sofia Papagiannaki
449 22dab079 Antony Chazapis
def get_content_range(request):
450 58a6c894 Antony Chazapis
    """Parse a Content-Range header from the request.
451 2715ade4 Sofia Papagiannaki

452 22dab079 Antony Chazapis
    Either returns None, when the header is not existent or should be ignored,
453 22dab079 Antony Chazapis
    or an (offset, length, total) tuple - check as length, total may be None.
454 22dab079 Antony Chazapis
    Returns (None, None, None) if the provided range is '*/*'.
455 22dab079 Antony Chazapis
    """
456 2715ade4 Sofia Papagiannaki
457 22dab079 Antony Chazapis
    ranges = request.META.get('HTTP_CONTENT_RANGE', '')
458 22dab079 Antony Chazapis
    if not ranges:
459 22dab079 Antony Chazapis
        return None
460 2715ade4 Sofia Papagiannaki
461 22dab079 Antony Chazapis
    p = re.compile('^bytes (?P<offset>\d+)-(?P<upto>\d*)/(?P<total>(\d+|\*))$')
462 22dab079 Antony Chazapis
    m = p.match(ranges)
463 22dab079 Antony Chazapis
    if not m:
464 22dab079 Antony Chazapis
        if ranges == 'bytes */*':
465 22dab079 Antony Chazapis
            return (None, None, None)
466 22dab079 Antony Chazapis
        return None
467 22dab079 Antony Chazapis
    offset = int(m.group('offset'))
468 22dab079 Antony Chazapis
    upto = m.group('upto')
469 22dab079 Antony Chazapis
    total = m.group('total')
470 22dab079 Antony Chazapis
    if upto != '':
471 22dab079 Antony Chazapis
        upto = int(upto)
472 b956618e Antony Chazapis
    else:
473 22dab079 Antony Chazapis
        upto = None
474 22dab079 Antony Chazapis
    if total != '*':
475 22dab079 Antony Chazapis
        total = int(total)
476 22dab079 Antony Chazapis
    else:
477 22dab079 Antony Chazapis
        total = None
478 70e526a0 Antony Chazapis
    if (upto is not None and offset > upto) or \
479 70e526a0 Antony Chazapis
        (total is not None and offset >= total) or \
480 2715ade4 Sofia Papagiannaki
            (total is not None and upto is not None and upto >= total):
481 22dab079 Antony Chazapis
        return None
482 2715ade4 Sofia Papagiannaki
483 70e526a0 Antony Chazapis
    if upto is None:
484 22dab079 Antony Chazapis
        length = None
485 22dab079 Antony Chazapis
    else:
486 22dab079 Antony Chazapis
        length = upto - offset + 1
487 22dab079 Antony Chazapis
    return (offset, length, total)
488 b956618e Antony Chazapis
489 2715ade4 Sofia Papagiannaki
490 3436eeb0 Antony Chazapis
def get_sharing(request):
491 3436eeb0 Antony Chazapis
    """Parse an X-Object-Sharing header from the request.
492 2715ade4 Sofia Papagiannaki

493 3436eeb0 Antony Chazapis
    Raises BadRequest on error.
494 3436eeb0 Antony Chazapis
    """
495 2715ade4 Sofia Papagiannaki
496 3436eeb0 Antony Chazapis
    permissions = request.META.get('HTTP_X_OBJECT_SHARING')
497 cca6c617 Antony Chazapis
    if permissions is None:
498 3436eeb0 Antony Chazapis
        return None
499 2715ade4 Sofia Papagiannaki
500 0a7f1671 Antony Chazapis
    # TODO: Document or remove '~' replacing.
501 0a7f1671 Antony Chazapis
    permissions = permissions.replace('~', '')
502 2715ade4 Sofia Papagiannaki
503 3436eeb0 Antony Chazapis
    ret = {}
504 cca6c617 Antony Chazapis
    permissions = permissions.replace(' ', '')
505 cca6c617 Antony Chazapis
    if permissions == '':
506 cca6c617 Antony Chazapis
        return ret
507 cca6c617 Antony Chazapis
    for perm in (x for x in permissions.split(';')):
508 cca6c617 Antony Chazapis
        if perm.startswith('read='):
509 2715ade4 Sofia Papagiannaki
            ret['read'] = list(set(
510 2715ade4 Sofia Papagiannaki
                [v.replace(' ', '').lower() for v in perm[5:].split(',')]))
511 02c0c3fa Antony Chazapis
            if '' in ret['read']:
512 02c0c3fa Antony Chazapis
                ret['read'].remove('')
513 e8886082 Antony Chazapis
            if '*' in ret['read']:
514 e8886082 Antony Chazapis
                ret['read'] = ['*']
515 3436eeb0 Antony Chazapis
            if len(ret['read']) == 0:
516 3436eeb0 Antony Chazapis
                raise BadRequest('Bad X-Object-Sharing header value')
517 3436eeb0 Antony Chazapis
        elif perm.startswith('write='):
518 2715ade4 Sofia Papagiannaki
            ret['write'] = list(set(
519 2715ade4 Sofia Papagiannaki
                [v.replace(' ', '').lower() for v in perm[6:].split(',')]))
520 02c0c3fa Antony Chazapis
            if '' in ret['write']:
521 02c0c3fa Antony Chazapis
                ret['write'].remove('')
522 e8886082 Antony Chazapis
            if '*' in ret['write']:
523 e8886082 Antony Chazapis
                ret['write'] = ['*']
524 3436eeb0 Antony Chazapis
            if len(ret['write']) == 0:
525 3436eeb0 Antony Chazapis
                raise BadRequest('Bad X-Object-Sharing header value')
526 3436eeb0 Antony Chazapis
        else:
527 3436eeb0 Antony Chazapis
            raise BadRequest('Bad X-Object-Sharing header value')
528 2715ade4 Sofia Papagiannaki
529 21a8a6ff Antony Chazapis
    # Keep duplicates only in write list.
530 2715ade4 Sofia Papagiannaki
    dups = [x for x in ret.get(
531 2715ade4 Sofia Papagiannaki
        'read', []) if x in ret.get('write', []) and x != '*']
532 21a8a6ff Antony Chazapis
    if dups:
533 21a8a6ff Antony Chazapis
        for x in dups:
534 21a8a6ff Antony Chazapis
            ret['read'].remove(x)
535 21a8a6ff Antony Chazapis
        if len(ret['read']) == 0:
536 21a8a6ff Antony Chazapis
            del(ret['read'])
537 2715ade4 Sofia Papagiannaki
538 3436eeb0 Antony Chazapis
    return ret
539 3436eeb0 Antony Chazapis
540 2715ade4 Sofia Papagiannaki
541 3ab38c43 Antony Chazapis
def get_public(request):
542 3ab38c43 Antony Chazapis
    """Parse an X-Object-Public header from the request.
543 2715ade4 Sofia Papagiannaki

544 3ab38c43 Antony Chazapis
    Raises BadRequest on error.
545 3ab38c43 Antony Chazapis
    """
546 2715ade4 Sofia Papagiannaki
547 3ab38c43 Antony Chazapis
    public = request.META.get('HTTP_X_OBJECT_PUBLIC')
548 3ab38c43 Antony Chazapis
    if public is None:
549 3ab38c43 Antony Chazapis
        return None
550 2715ade4 Sofia Papagiannaki
551 3ab38c43 Antony Chazapis
    public = public.replace(' ', '').lower()
552 3ab38c43 Antony Chazapis
    if public == 'true':
553 3ab38c43 Antony Chazapis
        return True
554 3ab38c43 Antony Chazapis
    elif public == 'false' or public == '':
555 3ab38c43 Antony Chazapis
        return False
556 3ab38c43 Antony Chazapis
    raise BadRequest('Bad X-Object-Public header value')
557 3ab38c43 Antony Chazapis
558 2715ade4 Sofia Papagiannaki
559 b956618e Antony Chazapis
def raw_input_socket(request):
560 58a6c894 Antony Chazapis
    """Return the socket for reading the rest of the request."""
561 2715ade4 Sofia Papagiannaki
562 b956618e Antony Chazapis
    server_software = request.META.get('SERVER_SOFTWARE')
563 fc1b2a75 Antony Chazapis
    if server_software and server_software.startswith('mod_python'):
564 b956618e Antony Chazapis
        return request._req
565 fc1b2a75 Antony Chazapis
    if 'wsgi.input' in request.environ:
566 fc1b2a75 Antony Chazapis
        return request.environ['wsgi.input']
567 08de868d Antony Chazapis
    raise NotImplemented('Unknown server software')
568 b956618e Antony Chazapis
569 2715ade4 Sofia Papagiannaki
MAX_UPLOAD_SIZE = 5 * (1024 * 1024 * 1024)  # 5GB
570 2715ade4 Sofia Papagiannaki
571 b956618e Antony Chazapis
572 c032f34d Antony Chazapis
def socket_read_iterator(request, length=0, blocksize=4096):
573 58a6c894 Antony Chazapis
    """Return a maximum of blocksize data read from the socket in each iteration.
574 2715ade4 Sofia Papagiannaki

575 22dab079 Antony Chazapis
    Read up to 'length'. If 'length' is negative, will attempt a chunked read.
576 b956618e Antony Chazapis
    The maximum ammount of data read is controlled by MAX_UPLOAD_SIZE.
577 b956618e Antony Chazapis
    """
578 2715ade4 Sofia Papagiannaki
579 c032f34d Antony Chazapis
    sock = raw_input_socket(request)
580 2715ade4 Sofia Papagiannaki
    if length < 0:  # Chunked transfers
581 c032f34d Antony Chazapis
        # Small version (server does the dechunking).
582 032dc768 Antony Chazapis
        if request.environ.get('mod_wsgi.input_chunked', None) or request.META['SERVER_SOFTWARE'].startswith('gunicorn'):
583 c032f34d Antony Chazapis
            while length < MAX_UPLOAD_SIZE:
584 c032f34d Antony Chazapis
                data = sock.read(blocksize)
585 c032f34d Antony Chazapis
                if data == '':
586 c032f34d Antony Chazapis
                    return
587 c032f34d Antony Chazapis
                yield data
588 c032f34d Antony Chazapis
            raise BadRequest('Maximum size is reached')
589 2715ade4 Sofia Papagiannaki
590 c032f34d Antony Chazapis
        # Long version (do the dechunking).
591 22dab079 Antony Chazapis
        data = ''
592 b956618e Antony Chazapis
        while length < MAX_UPLOAD_SIZE:
593 22dab079 Antony Chazapis
            # Get chunk size.
594 22dab079 Antony Chazapis
            if hasattr(sock, 'readline'):
595 22dab079 Antony Chazapis
                chunk_length = sock.readline()
596 22dab079 Antony Chazapis
            else:
597 22dab079 Antony Chazapis
                chunk_length = ''
598 22dab079 Antony Chazapis
                while chunk_length[-1:] != '\n':
599 22dab079 Antony Chazapis
                    chunk_length += sock.read(1)
600 22dab079 Antony Chazapis
                chunk_length.strip()
601 b956618e Antony Chazapis
            pos = chunk_length.find(';')
602 b956618e Antony Chazapis
            if pos >= 0:
603 b956618e Antony Chazapis
                chunk_length = chunk_length[:pos]
604 b956618e Antony Chazapis
            try:
605 b956618e Antony Chazapis
                chunk_length = int(chunk_length, 16)
606 b956618e Antony Chazapis
            except Exception, e:
607 2715ade4 Sofia Papagiannaki
                raise BadRequest('Bad chunk size')
608 2715ade4 Sofia Papagiannaki
                                 # TODO: Change to something more appropriate.
609 22dab079 Antony Chazapis
            # Check if done.
610 b956618e Antony Chazapis
            if chunk_length == 0:
611 22dab079 Antony Chazapis
                if len(data) > 0:
612 22dab079 Antony Chazapis
                    yield data
613 b956618e Antony Chazapis
                return
614 22dab079 Antony Chazapis
            # Get the actual data.
615 b956618e Antony Chazapis
            while chunk_length > 0:
616 22dab079 Antony Chazapis
                chunk = sock.read(min(chunk_length, blocksize))
617 22dab079 Antony Chazapis
                chunk_length -= len(chunk)
618 623a0cf4 Sofia Papagiannaki
                if length > 0:
619 623a0cf4 Sofia Papagiannaki
                    length += len(chunk)
620 22dab079 Antony Chazapis
                data += chunk
621 22dab079 Antony Chazapis
                if len(data) >= blocksize:
622 22dab079 Antony Chazapis
                    ret = data[:blocksize]
623 22dab079 Antony Chazapis
                    data = data[blocksize:]
624 22dab079 Antony Chazapis
                    yield ret
625 2715ade4 Sofia Papagiannaki
            sock.read(2)  # CRLF
626 32a437b1 Sofia Papagiannaki
        raise BadRequest('Maximum size is reached')
627 b956618e Antony Chazapis
    else:
628 b956618e Antony Chazapis
        if length > MAX_UPLOAD_SIZE:
629 32a437b1 Sofia Papagiannaki
            raise BadRequest('Maximum size is reached')
630 b956618e Antony Chazapis
        while length > 0:
631 b956618e Antony Chazapis
            data = sock.read(min(length, blocksize))
632 7b25e082 Antony Chazapis
            if not data:
633 7b25e082 Antony Chazapis
                raise BadRequest()
634 b956618e Antony Chazapis
            length -= len(data)
635 b956618e Antony Chazapis
            yield data
636 b956618e Antony Chazapis
637 2715ade4 Sofia Papagiannaki
638 817890f2 Antony Chazapis
class SaveToBackendHandler(FileUploadHandler):
639 817890f2 Antony Chazapis
    """Handle a file from an HTML form the django way."""
640 2715ade4 Sofia Papagiannaki
641 817890f2 Antony Chazapis
    def __init__(self, request=None):
642 817890f2 Antony Chazapis
        super(SaveToBackendHandler, self).__init__(request)
643 817890f2 Antony Chazapis
        self.backend = request.backend
644 2715ade4 Sofia Papagiannaki
645 817890f2 Antony Chazapis
    def put_data(self, length):
646 817890f2 Antony Chazapis
        if len(self.data) >= length:
647 817890f2 Antony Chazapis
            block = self.data[:length]
648 817890f2 Antony Chazapis
            self.file.hashmap.append(self.backend.put_block(block))
649 817890f2 Antony Chazapis
            self.md5.update(block)
650 817890f2 Antony Chazapis
            self.data = self.data[length:]
651 2715ade4 Sofia Papagiannaki
652 817890f2 Antony Chazapis
    def new_file(self, field_name, file_name, content_type, content_length, charset=None):
653 2715ade4 Sofia Papagiannaki
        self.md5 = hashlib.md5()
654 817890f2 Antony Chazapis
        self.data = ''
655 2715ade4 Sofia Papagiannaki
        self.file = UploadedFile(
656 2715ade4 Sofia Papagiannaki
            name=file_name, content_type=content_type, charset=charset)
657 817890f2 Antony Chazapis
        self.file.size = 0
658 817890f2 Antony Chazapis
        self.file.hashmap = []
659 2715ade4 Sofia Papagiannaki
660 817890f2 Antony Chazapis
    def receive_data_chunk(self, raw_data, start):
661 817890f2 Antony Chazapis
        self.data += raw_data
662 817890f2 Antony Chazapis
        self.file.size += len(raw_data)
663 817890f2 Antony Chazapis
        self.put_data(self.request.backend.block_size)
664 817890f2 Antony Chazapis
        return None
665 2715ade4 Sofia Papagiannaki
666 817890f2 Antony Chazapis
    def file_complete(self, file_size):
667 817890f2 Antony Chazapis
        l = len(self.data)
668 817890f2 Antony Chazapis
        if l > 0:
669 817890f2 Antony Chazapis
            self.put_data(l)
670 817890f2 Antony Chazapis
        self.file.etag = self.md5.hexdigest().lower()
671 817890f2 Antony Chazapis
        return self.file
672 817890f2 Antony Chazapis
673 2715ade4 Sofia Papagiannaki
674 22dab079 Antony Chazapis
class ObjectWrapper(object):
675 58a6c894 Antony Chazapis
    """Return the object's data block-per-block in each iteration.
676 2715ade4 Sofia Papagiannaki

677 22dab079 Antony Chazapis
    Read from the object using the offset and length provided in each entry of the range list.
678 22dab079 Antony Chazapis
    """
679 2715ade4 Sofia Papagiannaki
680 39593b2b Giorgos Verigakis
    def __init__(self, backend, ranges, sizes, hashmaps, boundary):
681 39593b2b Giorgos Verigakis
        self.backend = backend
682 22dab079 Antony Chazapis
        self.ranges = ranges
683 8cb45c13 Antony Chazapis
        self.sizes = sizes
684 8cb45c13 Antony Chazapis
        self.hashmaps = hashmaps
685 22dab079 Antony Chazapis
        self.boundary = boundary
686 8cb45c13 Antony Chazapis
        self.size = sum(self.sizes)
687 2715ade4 Sofia Papagiannaki
688 8cb45c13 Antony Chazapis
        self.file_index = 0
689 8cb45c13 Antony Chazapis
        self.block_index = 0
690 8cb45c13 Antony Chazapis
        self.block_hash = -1
691 22dab079 Antony Chazapis
        self.block = ''
692 2715ade4 Sofia Papagiannaki
693 22dab079 Antony Chazapis
        self.range_index = -1
694 22dab079 Antony Chazapis
        self.offset, self.length = self.ranges[0]
695 2715ade4 Sofia Papagiannaki
696 22dab079 Antony Chazapis
    def __iter__(self):
697 22dab079 Antony Chazapis
        return self
698 2715ade4 Sofia Papagiannaki
699 22dab079 Antony Chazapis
    def part_iterator(self):
700 22dab079 Antony Chazapis
        if self.length > 0:
701 8cb45c13 Antony Chazapis
            # Get the file for the current offset.
702 8cb45c13 Antony Chazapis
            file_size = self.sizes[self.file_index]
703 8cb45c13 Antony Chazapis
            while self.offset >= file_size:
704 8cb45c13 Antony Chazapis
                self.offset -= file_size
705 8cb45c13 Antony Chazapis
                self.file_index += 1
706 8cb45c13 Antony Chazapis
                file_size = self.sizes[self.file_index]
707 2715ade4 Sofia Papagiannaki
708 8cb45c13 Antony Chazapis
            # Get the block for the current position.
709 39593b2b Giorgos Verigakis
            self.block_index = int(self.offset / self.backend.block_size)
710 8cb45c13 Antony Chazapis
            if self.block_hash != self.hashmaps[self.file_index][self.block_index]:
711 2715ade4 Sofia Papagiannaki
                self.block_hash = self.hashmaps[
712 2715ade4 Sofia Papagiannaki
                    self.file_index][self.block_index]
713 22dab079 Antony Chazapis
                try:
714 39593b2b Giorgos Verigakis
                    self.block = self.backend.get_block(self.block_hash)
715 7efc9f86 Sofia Papagiannaki
                except ItemNotExists:
716 22dab079 Antony Chazapis
                    raise ItemNotFound('Block does not exist')
717 2715ade4 Sofia Papagiannaki
718 22dab079 Antony Chazapis
            # Get the data from the block.
719 39593b2b Giorgos Verigakis
            bo = self.offset % self.backend.block_size
720 c9865fe1 Antony Chazapis
            bs = self.backend.block_size
721 45cf0bc8 Antony Chazapis
            if (self.block_index == len(self.hashmaps[self.file_index]) - 1 and
722 2715ade4 Sofia Papagiannaki
                    self.sizes[self.file_index] % self.backend.block_size):
723 c9865fe1 Antony Chazapis
                bs = self.sizes[self.file_index] % self.backend.block_size
724 c9865fe1 Antony Chazapis
            bl = min(self.length, bs - bo)
725 22dab079 Antony Chazapis
            data = self.block[bo:bo + bl]
726 22dab079 Antony Chazapis
            self.offset += bl
727 22dab079 Antony Chazapis
            self.length -= bl
728 22dab079 Antony Chazapis
            return data
729 22dab079 Antony Chazapis
        else:
730 22dab079 Antony Chazapis
            raise StopIteration
731 2715ade4 Sofia Papagiannaki
732 22dab079 Antony Chazapis
    def next(self):
733 22dab079 Antony Chazapis
        if len(self.ranges) == 1:
734 22dab079 Antony Chazapis
            return self.part_iterator()
735 22dab079 Antony Chazapis
        if self.range_index == len(self.ranges):
736 22dab079 Antony Chazapis
            raise StopIteration
737 22dab079 Antony Chazapis
        try:
738 22dab079 Antony Chazapis
            if self.range_index == -1:
739 22dab079 Antony Chazapis
                raise StopIteration
740 22dab079 Antony Chazapis
            return self.part_iterator()
741 22dab079 Antony Chazapis
        except StopIteration:
742 22dab079 Antony Chazapis
            self.range_index += 1
743 22dab079 Antony Chazapis
            out = []
744 22dab079 Antony Chazapis
            if self.range_index < len(self.ranges):
745 22dab079 Antony Chazapis
                # Part header.
746 22dab079 Antony Chazapis
                self.offset, self.length = self.ranges[self.range_index]
747 8cb45c13 Antony Chazapis
                self.file_index = 0
748 22dab079 Antony Chazapis
                if self.range_index > 0:
749 22dab079 Antony Chazapis
                    out.append('')
750 22dab079 Antony Chazapis
                out.append('--' + self.boundary)
751 2715ade4 Sofia Papagiannaki
                out.append('Content-Range: bytes %d-%d/%d' % (
752 2715ade4 Sofia Papagiannaki
                    self.offset, self.offset + self.length - 1, self.size))
753 22dab079 Antony Chazapis
                out.append('Content-Transfer-Encoding: binary')
754 22dab079 Antony Chazapis
                out.append('')
755 22dab079 Antony Chazapis
                out.append('')
756 22dab079 Antony Chazapis
                return '\r\n'.join(out)
757 22dab079 Antony Chazapis
            else:
758 22dab079 Antony Chazapis
                # Footer.
759 22dab079 Antony Chazapis
                out.append('')
760 22dab079 Antony Chazapis
                out.append('--' + self.boundary + '--')
761 22dab079 Antony Chazapis
                out.append('')
762 22dab079 Antony Chazapis
                return '\r\n'.join(out)
763 22dab079 Antony Chazapis
764 2715ade4 Sofia Papagiannaki
765 8cb45c13 Antony Chazapis
def object_data_response(request, sizes, hashmaps, meta, public=False):
766 7bef5750 Antony Chazapis
    """Get the HttpResponse object for replying with the object's data."""
767 2715ade4 Sofia Papagiannaki
768 7bef5750 Antony Chazapis
    # Range handling.
769 8cb45c13 Antony Chazapis
    size = sum(sizes)
770 7bef5750 Antony Chazapis
    ranges = get_range(request, size)
771 7bef5750 Antony Chazapis
    if ranges is None:
772 7bef5750 Antony Chazapis
        ranges = [(0, size)]
773 7bef5750 Antony Chazapis
        ret = 200
774 7bef5750 Antony Chazapis
    else:
775 7bef5750 Antony Chazapis
        check = [True for offset, length in ranges if
776 2715ade4 Sofia Papagiannaki
                 length <= 0 or length > size or
777 2715ade4 Sofia Papagiannaki
                 offset < 0 or offset >= size or
778 2715ade4 Sofia Papagiannaki
                 offset + length > size]
779 7bef5750 Antony Chazapis
        if len(check) > 0:
780 6d1e6dce Sofia Papagiannaki
            raise RangeNotSatisfiable('Requested range exceeds object limits')
781 7bef5750 Antony Chazapis
        ret = 206
782 15d465b8 Antony Chazapis
        if_range = request.META.get('HTTP_IF_RANGE')
783 15d465b8 Antony Chazapis
        if if_range:
784 6d1e6dce Sofia Papagiannaki
            try:
785 15d465b8 Antony Chazapis
                # Modification time has passed instead.
786 6d1e6dce Sofia Papagiannaki
                last_modified = parse_http_date(if_range)
787 6d1e6dce Sofia Papagiannaki
                if last_modified != meta['modified']:
788 6d1e6dce Sofia Papagiannaki
                    ranges = [(0, size)]
789 6d1e6dce Sofia Papagiannaki
                    ret = 200
790 6d1e6dce Sofia Papagiannaki
            except ValueError:
791 33b4e4a6 Antony Chazapis
                if if_range != meta['checksum']:
792 6d1e6dce Sofia Papagiannaki
                    ranges = [(0, size)]
793 6d1e6dce Sofia Papagiannaki
                    ret = 200
794 2715ade4 Sofia Papagiannaki
795 7bef5750 Antony Chazapis
    if ret == 206 and len(ranges) > 1:
796 7bef5750 Antony Chazapis
        boundary = uuid.uuid4().hex
797 7bef5750 Antony Chazapis
    else:
798 7bef5750 Antony Chazapis
        boundary = ''
799 39593b2b Giorgos Verigakis
    wrapper = ObjectWrapper(request.backend, ranges, sizes, hashmaps, boundary)
800 7bef5750 Antony Chazapis
    response = HttpResponse(wrapper, status=ret)
801 02c0c3fa Antony Chazapis
    put_object_headers(response, meta, public)
802 7bef5750 Antony Chazapis
    if ret == 206:
803 7bef5750 Antony Chazapis
        if len(ranges) == 1:
804 7bef5750 Antony Chazapis
            offset, length = ranges[0]
805 2715ade4 Sofia Papagiannaki
            response[
806 2715ade4 Sofia Papagiannaki
                'Content-Length'] = length  # Update with the correct length.
807 2715ade4 Sofia Papagiannaki
            response['Content-Range'] = 'bytes %d-%d/%d' % (
808 2715ade4 Sofia Papagiannaki
                offset, offset + length - 1, size)
809 7bef5750 Antony Chazapis
        else:
810 7bef5750 Antony Chazapis
            del(response['Content-Length'])
811 2715ade4 Sofia Papagiannaki
            response['Content-Type'] = 'multipart/byteranges; boundary=%s' % (
812 2715ade4 Sofia Papagiannaki
                boundary,)
813 7bef5750 Antony Chazapis
    return response
814 7bef5750 Antony Chazapis
815 2715ade4 Sofia Papagiannaki
816 39593b2b Giorgos Verigakis
def put_object_block(request, hashmap, data, offset):
817 cb146cf9 Antony Chazapis
    """Put one block of data at the given offset."""
818 2715ade4 Sofia Papagiannaki
819 39593b2b Giorgos Verigakis
    bi = int(offset / request.backend.block_size)
820 39593b2b Giorgos Verigakis
    bo = offset % request.backend.block_size
821 39593b2b Giorgos Verigakis
    bl = min(len(data), request.backend.block_size - bo)
822 cb146cf9 Antony Chazapis
    if bi < len(hashmap):
823 39593b2b Giorgos Verigakis
        hashmap[bi] = request.backend.update_block(hashmap[bi], data[:bl], bo)
824 cb146cf9 Antony Chazapis
    else:
825 39593b2b Giorgos Verigakis
        hashmap.append(request.backend.put_block(('\x00' * bo) + data[:bl]))
826 2715ade4 Sofia Papagiannaki
    return bl  # Return ammount of data written.
827 2715ade4 Sofia Papagiannaki
828 cb146cf9 Antony Chazapis
829 b3155065 Antony Chazapis
def hashmap_md5(backend, hashmap, size):
830 cddcf432 chazapis
    """Produce the MD5 sum from the data in the hashmap."""
831 2715ade4 Sofia Papagiannaki
832 cddcf432 chazapis
    # TODO: Search backend for the MD5 of another object with the same hashmap and size...
833 cddcf432 chazapis
    md5 = hashlib.md5()
834 b3155065 Antony Chazapis
    bs = backend.block_size
835 cddcf432 chazapis
    for bi, hash in enumerate(hashmap):
836 2715ade4 Sofia Papagiannaki
        data = backend.get_block(hash)  # Blocks come in padded.
837 cddcf432 chazapis
        if bi == len(hashmap) - 1:
838 c9865fe1 Antony Chazapis
            data = data[:size % bs]
839 c9865fe1 Antony Chazapis
        md5.update(data)
840 cddcf432 chazapis
    return md5.hexdigest().lower()
841 49133392 Antony Chazapis
842 2715ade4 Sofia Papagiannaki
843 af7bb62f Antony Chazapis
def simple_list_response(request, l):
844 6b6b6c1e Antony Chazapis
    if request.serialization == 'text':
845 6b6b6c1e Antony Chazapis
        return '\n'.join(l) + '\n'
846 6b6b6c1e Antony Chazapis
    if request.serialization == 'xml':
847 af7bb62f Antony Chazapis
        return render_to_string('items.xml', {'items': l})
848 6b6b6c1e Antony Chazapis
    if request.serialization == 'json':
849 6b6b6c1e Antony Chazapis
        return json.dumps(l)
850 6b6b6c1e Antony Chazapis
851 2715ade4 Sofia Papagiannaki
852 717adf72 Sofia Papagiannaki
853 35d42381 Vangelis Koukis
def _get_backend():
854 a7dff008 Antony Chazapis
    backend = connect_backend(db_module=BACKEND_DB_MODULE,
855 a7dff008 Antony Chazapis
                              db_connection=BACKEND_DB_CONNECTION,
856 a7dff008 Antony Chazapis
                              block_module=BACKEND_BLOCK_MODULE,
857 a7dff008 Antony Chazapis
                              block_path=BACKEND_BLOCK_PATH,
858 1427ce62 Sofia Papagiannaki
                              block_umask=BACKEND_BLOCK_UMASK,
859 a7dff008 Antony Chazapis
                              queue_module=BACKEND_QUEUE_MODULE,
860 f4fbb0fa Sofia Papagiannaki
                              queue_hosts=BACKEND_QUEUE_HOSTS,
861 f4fbb0fa Sofia Papagiannaki
                              queue_exchange=BACKEND_QUEUE_EXCHANGE)
862 a7dff008 Antony Chazapis
    backend.default_policy['quota'] = BACKEND_QUOTA
863 a7dff008 Antony Chazapis
    backend.default_policy['versioning'] = BACKEND_VERSIONING
864 228de81b Antony Chazapis
    return backend
865 228de81b Antony Chazapis
866 2715ade4 Sofia Papagiannaki
867 35d42381 Vangelis Koukis
def _pooled_backend_close(backend):
868 35d42381 Vangelis Koukis
    backend._pool.pool_put(backend)
869 35d42381 Vangelis Koukis
870 35d42381 Vangelis Koukis
871 35d42381 Vangelis Koukis
from synnefo.lib.pool import ObjectPool
872 35d42381 Vangelis Koukis
from new import instancemethod
873 35d42381 Vangelis Koukis
874 35d42381 Vangelis Koukis
USAGE_LIMIT = 500
875 35d42381 Vangelis Koukis
POOL_SIZE = 5
876 35d42381 Vangelis Koukis
877 35d42381 Vangelis Koukis
class PithosBackendPool(ObjectPool):
878 35d42381 Vangelis Koukis
    def _pool_create(self):
879 35d42381 Vangelis Koukis
        backend = _get_backend()
880 35d42381 Vangelis Koukis
        backend._real_close = backend.close
881 35d42381 Vangelis Koukis
        backend.close = instancemethod(_pooled_backend_close, backend,
882 35d42381 Vangelis Koukis
                                       type(backend))
883 35d42381 Vangelis Koukis
        backend._pool = self
884 35d42381 Vangelis Koukis
        backend._use_count = USAGE_LIMIT
885 35d42381 Vangelis Koukis
        return backend
886 35d42381 Vangelis Koukis
887 35d42381 Vangelis Koukis
    def _pool_cleanup(self, backend):
888 35d42381 Vangelis Koukis
        c = backend._use_count - 1
889 35d42381 Vangelis Koukis
        if c < 0:
890 35d42381 Vangelis Koukis
            backend._real_close()
891 35d42381 Vangelis Koukis
            return True
892 35d42381 Vangelis Koukis
893 35d42381 Vangelis Koukis
        backend._use_count = c
894 35d42381 Vangelis Koukis
        return False
895 35d42381 Vangelis Koukis
896 35d42381 Vangelis Koukis
_pithos_backend_pool = PithosBackendPool(size=POOL_SIZE)
897 35d42381 Vangelis Koukis
898 35d42381 Vangelis Koukis
899 35d42381 Vangelis Koukis
def get_backend():
900 35d42381 Vangelis Koukis
    return _pithos_backend_pool.pool_get()
901 35d42381 Vangelis Koukis
902 35d42381 Vangelis Koukis
903 9fefc052 Antony Chazapis
def update_request_headers(request):
904 9fefc052 Antony Chazapis
    # Handle URL-encoded keys and values.
905 2715ade4 Sofia Papagiannaki
    meta = dict([(
906 2715ade4 Sofia Papagiannaki
        k, v) for k, v in request.META.iteritems() if k.startswith('HTTP_')])
907 88283e9e Antony Chazapis
    for k, v in meta.iteritems():
908 88283e9e Antony Chazapis
        try:
909 88283e9e Antony Chazapis
            k.decode('ascii')
910 88283e9e Antony Chazapis
            v.decode('ascii')
911 88283e9e Antony Chazapis
        except UnicodeDecodeError:
912 88283e9e Antony Chazapis
            raise BadRequest('Bad character in headers.')
913 88283e9e Antony Chazapis
        if '%' in k or '%' in v:
914 88283e9e Antony Chazapis
            del(request.META[k])
915 2715ade4 Sofia Papagiannaki
            request.META[unquote(k)] = smart_unicode(unquote(
916 2715ade4 Sofia Papagiannaki
                v), strings_only=True)
917 2715ade4 Sofia Papagiannaki
918 9fefc052 Antony Chazapis
919 b956618e Antony Chazapis
def update_response_headers(request, response):
920 b956618e Antony Chazapis
    if request.serialization == 'xml':
921 b956618e Antony Chazapis
        response['Content-Type'] = 'application/xml; charset=UTF-8'
922 b956618e Antony Chazapis
    elif request.serialization == 'json':
923 b956618e Antony Chazapis
        response['Content-Type'] = 'application/json; charset=UTF-8'
924 22dab079 Antony Chazapis
    elif not response['Content-Type']:
925 b956618e Antony Chazapis
        response['Content-Type'] = 'text/plain; charset=UTF-8'
926 2715ade4 Sofia Papagiannaki
927 9fefc052 Antony Chazapis
    if (not response.has_header('Content-Length') and
928 9fefc052 Antony Chazapis
        not (response.has_header('Content-Type') and
929 9fefc052 Antony Chazapis
             response['Content-Type'].startswith('multipart/byteranges'))):
930 b980169d Antony Chazapis
        response['Content-Length'] = len(response.content)
931 2715ade4 Sofia Papagiannaki
932 9fefc052 Antony Chazapis
    # URL-encode unicode in headers.
933 9fefc052 Antony Chazapis
    meta = response.items()
934 9fefc052 Antony Chazapis
    for k, v in meta:
935 88283e9e Antony Chazapis
        if (k.startswith('X-Account-') or k.startswith('X-Container-') or
936 2715ade4 Sofia Papagiannaki
                k.startswith('X-Object-') or k.startswith('Content-')):
937 9fefc052 Antony Chazapis
            del(response[k])
938 88283e9e Antony Chazapis
            response[quote(k)] = quote(v, safe='/=,:@; ')
939 b956618e Antony Chazapis
940 2715ade4 Sofia Papagiannaki
941 b956618e Antony Chazapis
def render_fault(request, fault):
942 a7dff008 Antony Chazapis
    if isinstance(fault, InternalServerError) and settings.DEBUG:
943 b956618e Antony Chazapis
        fault.details = format_exc(fault)
944 2715ade4 Sofia Papagiannaki
945 b956618e Antony Chazapis
    request.serialization = 'text'
946 08de868d Antony Chazapis
    data = fault.message + '\n'
947 08de868d Antony Chazapis
    if fault.details:
948 08de868d Antony Chazapis
        data += '\n' + fault.details
949 b956618e Antony Chazapis
    response = HttpResponse(data, status=fault.code)
950 b956618e Antony Chazapis
    update_response_headers(request, response)
951 b956618e Antony Chazapis
    return response
952 b956618e Antony Chazapis
953 2715ade4 Sofia Papagiannaki
954 b956618e Antony Chazapis
def request_serialization(request, format_allowed=False):
955 58a6c894 Antony Chazapis
    """Return the serialization format requested.
956 2715ade4 Sofia Papagiannaki

957 b956618e Antony Chazapis
    Valid formats are 'text' and 'json', 'xml' if 'format_allowed' is True.
958 b956618e Antony Chazapis
    """
959 2715ade4 Sofia Papagiannaki
960 b956618e Antony Chazapis
    if not format_allowed:
961 b956618e Antony Chazapis
        return 'text'
962 2715ade4 Sofia Papagiannaki
963 b956618e Antony Chazapis
    format = request.GET.get('format')
964 b956618e Antony Chazapis
    if format == 'json':
965 b956618e Antony Chazapis
        return 'json'
966 b956618e Antony Chazapis
    elif format == 'xml':
967 b956618e Antony Chazapis
        return 'xml'
968 2715ade4 Sofia Papagiannaki
969 f9f15f92 Antony Chazapis
    for item in request.META.get('HTTP_ACCEPT', '').split(','):
970 f9f15f92 Antony Chazapis
        accept, sep, rest = item.strip().partition(';')
971 f9f15f92 Antony Chazapis
        if accept == 'application/json':
972 f9f15f92 Antony Chazapis
            return 'json'
973 f9f15f92 Antony Chazapis
        elif accept == 'application/xml' or accept == 'text/xml':
974 f9f15f92 Antony Chazapis
            return 'xml'
975 2715ade4 Sofia Papagiannaki
976 b956618e Antony Chazapis
    return 'text'
977 b956618e Antony Chazapis
978 2715ade4 Sofia Papagiannaki
979 297513ba Antony Chazapis
def api_method(http_method=None, format_allowed=False, user_required=True):
980 58a6c894 Antony Chazapis
    """Decorator function for views that implement an API method."""
981 2715ade4 Sofia Papagiannaki
982 b956618e Antony Chazapis
    def decorator(func):
983 b956618e Antony Chazapis
        @wraps(func)
984 b956618e Antony Chazapis
        def wrapper(request, *args, **kwargs):
985 b956618e Antony Chazapis
            try:
986 b956618e Antony Chazapis
                if http_method and request.method != http_method:
987 b956618e Antony Chazapis
                    raise BadRequest('Method not allowed.')
988 2715ade4 Sofia Papagiannaki
989 dfdf4802 Sofia Papagiannaki
                if user_required:
990 dfdf4802 Sofia Papagiannaki
                    token = None
991 dfdf4802 Sofia Papagiannaki
                    if request.method in ('HEAD', 'GET') and COOKIE_NAME in request.COOKIES:
992 2715ade4 Sofia Papagiannaki
                        cookie_value = unquote(
993 2715ade4 Sofia Papagiannaki
                            request.COOKIES.get(COOKIE_NAME, ''))
994 dfdf4802 Sofia Papagiannaki
                        if cookie_value and '|' in cookie_value:
995 dfdf4802 Sofia Papagiannaki
                            token = cookie_value.split('|', 1)[1]
996 2715ade4 Sofia Papagiannaki
                    get_user(request,
997 2715ade4 Sofia Papagiannaki
                             AUTHENTICATION_URL, AUTHENTICATION_USERS, token)
998 dfdf4802 Sofia Papagiannaki
                    if  getattr(request, 'user', None) is None:
999 dfdf4802 Sofia Papagiannaki
                        raise Unauthorized('Access denied')
1000 2715ade4 Sofia Papagiannaki
1001 b956618e Antony Chazapis
                # The args variable may contain up to (account, container, object).
1002 b956618e Antony Chazapis
                if len(args) > 1 and len(args[1]) > 256:
1003 b956618e Antony Chazapis
                    raise BadRequest('Container name too large.')
1004 b956618e Antony Chazapis
                if len(args) > 2 and len(args[2]) > 1024:
1005 b956618e Antony Chazapis
                    raise BadRequest('Object name too large.')
1006 2715ade4 Sofia Papagiannaki
1007 9fefc052 Antony Chazapis
                # Format and check headers.
1008 9fefc052 Antony Chazapis
                update_request_headers(request)
1009 2715ade4 Sofia Papagiannaki
1010 b956618e Antony Chazapis
                # Fill in custom request variables.
1011 2715ade4 Sofia Papagiannaki
                request.serialization = request_serialization(
1012 2715ade4 Sofia Papagiannaki
                    request, format_allowed)
1013 228de81b Antony Chazapis
                request.backend = get_backend()
1014 2715ade4 Sofia Papagiannaki
1015 b956618e Antony Chazapis
                response = func(request, *args, **kwargs)
1016 b956618e Antony Chazapis
                update_response_headers(request, response)
1017 b956618e Antony Chazapis
                return response
1018 b956618e Antony Chazapis
            except Fault, fault:
1019 b956618e Antony Chazapis
                return render_fault(request, fault)
1020 b956618e Antony Chazapis
            except BaseException, e:
1021 b956618e Antony Chazapis
                logger.exception('Unexpected error: %s' % e)
1022 dfdf4802 Sofia Papagiannaki
                fault = InternalServerError('Unexpected error: %s' % e)
1023 b956618e Antony Chazapis
                return render_fault(request, fault)
1024 7b25e082 Antony Chazapis
            finally:
1025 297513ba Antony Chazapis
                if getattr(request, 'backend', None) is not None:
1026 d14fe290 Antony Chazapis
                    request.backend.close()
1027 b956618e Antony Chazapis
        return wrapper
1028 b956618e Antony Chazapis
    return decorator