Statistics
| Branch: | Tag: | Revision:

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

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 761c2b3c root
                                 BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH,
61 761c2b3c root
                                 BACKEND_BLOCK_UMASK,
62 f4fbb0fa Sofia Papagiannaki
                                 BACKEND_QUEUE_MODULE, BACKEND_QUEUE_HOSTS,
63 f4fbb0fa Sofia Papagiannaki
                                 BACKEND_QUEUE_EXCHANGE,
64 57e09fb4 root
                                 PITHOS_QUOTAHOLDER_URL,
65 8313c681 root
                                 BACKEND_QUOTA, BACKEND_VERSIONING,
66 b1dadd0e Sofia Papagiannaki
                                 BACKEND_FREE_VERSIONING,
67 761c2b3c root
                                 AUTHENTICATION_URL, AUTHENTICATION_USERS,
68 761c2b3c root
                                 SERVICE_TOKEN, COOKIE_NAME)
69 39593b2b Giorgos Verigakis
from pithos.backends import connect_backend
70 7efc9f86 Sofia Papagiannaki
from pithos.backends.base import NotAllowedError, QuotaError, ItemNotExists, VersionNotExists
71 b956618e Antony Chazapis
72 b956618e Antony Chazapis
import logging
73 22dab079 Antony Chazapis
import re
74 cbfb6636 Sofia Papagiannaki
import hashlib
75 7bef5750 Antony Chazapis
import uuid
76 c48acbfd Sofia Papagiannaki
import decimal
77 8c793655 Antony Chazapis
78 b956618e Antony Chazapis
79 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
80 b956618e Antony Chazapis
81 b956618e Antony Chazapis
82 e11087c2 Antony Chazapis
class UTC(tzinfo):
83 2715ade4 Sofia Papagiannaki
    def utcoffset(self, dt):
84 2715ade4 Sofia Papagiannaki
        return timedelta(0)
85 e11087c2 Antony Chazapis
86 2715ade4 Sofia Papagiannaki
    def tzname(self, dt):
87 2715ade4 Sofia Papagiannaki
        return 'UTC'
88 e11087c2 Antony Chazapis
89 2715ade4 Sofia Papagiannaki
    def dst(self, dt):
90 2715ade4 Sofia Papagiannaki
        return timedelta(0)
91 e11087c2 Antony Chazapis
92 e11087c2 Antony Chazapis
93 c48acbfd Sofia Papagiannaki
def json_encode_decimal(obj):
94 c48acbfd Sofia Papagiannaki
    if isinstance(obj, decimal.Decimal):
95 c48acbfd Sofia Papagiannaki
        return str(obj)
96 c48acbfd Sofia Papagiannaki
    raise TypeError(repr(obj) + " is not JSON serializable")
97 c48acbfd Sofia Papagiannaki
98 2715ade4 Sofia Papagiannaki
99 e11087c2 Antony Chazapis
def isoformat(d):
100 2715ade4 Sofia Papagiannaki
    """Return an ISO8601 date string that includes a timezone."""
101 2715ade4 Sofia Papagiannaki
102 2715ade4 Sofia Papagiannaki
    return d.replace(tzinfo=UTC()).isoformat()
103 e11087c2 Antony Chazapis
104 e11087c2 Antony Chazapis
105 804e8fe7 Antony Chazapis
def rename_meta_key(d, old, new):
106 804e8fe7 Antony Chazapis
    if old not in d:
107 804e8fe7 Antony Chazapis
        return
108 804e8fe7 Antony Chazapis
    d[new] = d[old]
109 804e8fe7 Antony Chazapis
    del(d[old])
110 804e8fe7 Antony Chazapis
111 2715ade4 Sofia Papagiannaki
112 02c0c3fa Antony Chazapis
def printable_header_dict(d):
113 b956618e Antony Chazapis
    """Format a meta dictionary for printing out json/xml.
114 2715ade4 Sofia Papagiannaki

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

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

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

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

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

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

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

932 b956618e Antony Chazapis
    Valid formats are 'text' and 'json', 'xml' if 'format_allowed' is True.
933 b956618e Antony Chazapis
    """
934 2715ade4 Sofia Papagiannaki
935 b956618e Antony Chazapis
    if not format_allowed:
936 b956618e Antony Chazapis
        return 'text'
937 2715ade4 Sofia Papagiannaki
938 b956618e Antony Chazapis
    format = request.GET.get('format')
939 b956618e Antony Chazapis
    if format == 'json':
940 b956618e Antony Chazapis
        return 'json'
941 b956618e Antony Chazapis
    elif format == 'xml':
942 b956618e Antony Chazapis
        return 'xml'
943 2715ade4 Sofia Papagiannaki
944 f9f15f92 Antony Chazapis
    for item in request.META.get('HTTP_ACCEPT', '').split(','):
945 f9f15f92 Antony Chazapis
        accept, sep, rest = item.strip().partition(';')
946 f9f15f92 Antony Chazapis
        if accept == 'application/json':
947 f9f15f92 Antony Chazapis
            return 'json'
948 f9f15f92 Antony Chazapis
        elif accept == 'application/xml' or accept == 'text/xml':
949 f9f15f92 Antony Chazapis
            return 'xml'
950 2715ade4 Sofia Papagiannaki
951 b956618e Antony Chazapis
    return 'text'
952 b956618e Antony Chazapis
953 b3102a96 Sofia Papagiannaki
class User(unicode):
954 d62295ba Sofia Papagiannaki
    pass
955 35d42381 Vangelis Koukis
956 297513ba Antony Chazapis
def api_method(http_method=None, format_allowed=False, user_required=True):
957 58a6c894 Antony Chazapis
    """Decorator function for views that implement an API method."""
958 2715ade4 Sofia Papagiannaki
959 b956618e Antony Chazapis
    def decorator(func):
960 b956618e Antony Chazapis
        @wraps(func)
961 b956618e Antony Chazapis
        def wrapper(request, *args, **kwargs):
962 b956618e Antony Chazapis
            try:
963 b956618e Antony Chazapis
                if http_method and request.method != http_method:
964 b956618e Antony Chazapis
                    raise BadRequest('Method not allowed.')
965 2715ade4 Sofia Papagiannaki
966 dfdf4802 Sofia Papagiannaki
                if user_required:
967 dfdf4802 Sofia Papagiannaki
                    token = None
968 dfdf4802 Sofia Papagiannaki
                    if request.method in ('HEAD', 'GET') and COOKIE_NAME in request.COOKIES:
969 2715ade4 Sofia Papagiannaki
                        cookie_value = unquote(
970 2715ade4 Sofia Papagiannaki
                            request.COOKIES.get(COOKIE_NAME, ''))
971 bf45cb4a Sofia Papagiannaki
                        account, sep, token = cookie_value.partition('|')
972 2715ade4 Sofia Papagiannaki
                    get_user(request,
973 2715ade4 Sofia Papagiannaki
                             AUTHENTICATION_URL, AUTHENTICATION_USERS, token)
974 dfdf4802 Sofia Papagiannaki
                    if  getattr(request, 'user', None) is None:
975 dfdf4802 Sofia Papagiannaki
                        raise Unauthorized('Access denied')
976 b3102a96 Sofia Papagiannaki
                    assert getattr(request, 'user_uniq', None) != None
977 b3102a96 Sofia Papagiannaki
                    request.user_uniq = User(request.user_uniq)
978 d62295ba Sofia Papagiannaki
                    request.user_uniq.id = request.user.get('id')
979 2c22e4ac Antony Chazapis
                
980 b956618e Antony Chazapis
                # The args variable may contain up to (account, container, object).
981 b956618e Antony Chazapis
                if len(args) > 1 and len(args[1]) > 256:
982 b956618e Antony Chazapis
                    raise BadRequest('Container name too large.')
983 b956618e Antony Chazapis
                if len(args) > 2 and len(args[2]) > 1024:
984 b956618e Antony Chazapis
                    raise BadRequest('Object name too large.')
985 2715ade4 Sofia Papagiannaki
986 9fefc052 Antony Chazapis
                # Format and check headers.
987 9fefc052 Antony Chazapis
                update_request_headers(request)
988 2715ade4 Sofia Papagiannaki
989 b956618e Antony Chazapis
                # Fill in custom request variables.
990 2715ade4 Sofia Papagiannaki
                request.serialization = request_serialization(
991 2715ade4 Sofia Papagiannaki
                    request, format_allowed)
992 228de81b Antony Chazapis
                request.backend = get_backend()
993 2715ade4 Sofia Papagiannaki
994 b956618e Antony Chazapis
                response = func(request, *args, **kwargs)
995 b956618e Antony Chazapis
                update_response_headers(request, response)
996 b956618e Antony Chazapis
                return response
997 b956618e Antony Chazapis
            except Fault, fault:
998 6e9c8fc1 Christos Stavrakakis
                if fault.code >= 500:
999 6e9c8fc1 Christos Stavrakakis
                    logger.exception("API Fault")
1000 b956618e Antony Chazapis
                return render_fault(request, fault)
1001 b956618e Antony Chazapis
            except BaseException, e:
1002 b956618e Antony Chazapis
                logger.exception('Unexpected error: %s' % e)
1003 dfdf4802 Sofia Papagiannaki
                fault = InternalServerError('Unexpected error: %s' % e)
1004 b956618e Antony Chazapis
                return render_fault(request, fault)
1005 7b25e082 Antony Chazapis
            finally:
1006 297513ba Antony Chazapis
                if getattr(request, 'backend', None) is not None:
1007 d14fe290 Antony Chazapis
                    request.backend.close()
1008 b956618e Antony Chazapis
        return wrapper
1009 b956618e Antony Chazapis
    return decorator