Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / util.py @ 6dd0fc7c

History | View | Annotate | Download (38 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 traceback import format_exc
36 65bbcd43 Christos Stavrakakis
from datetime import datetime
37 9fefc052 Antony Chazapis
from urllib import quote, unquote
38 b956618e Antony Chazapis
39 b956618e Antony Chazapis
from django.conf import settings
40 b956618e Antony Chazapis
from django.http import HttpResponse
41 6b6b6c1e Antony Chazapis
from django.template.loader import render_to_string
42 1993fea9 Antony Chazapis
from django.utils import simplejson as json
43 22dab079 Antony Chazapis
from django.utils.http import http_date, parse_etags
44 fb064032 Antony Chazapis
from django.utils.encoding import smart_unicode, smart_str
45 817890f2 Antony Chazapis
from django.core.files.uploadhandler import FileUploadHandler
46 817890f2 Antony Chazapis
from django.core.files.uploadedfile import UploadedFile
47 b956618e Antony Chazapis
48 896754a6 Christos Stavrakakis
from snf_django.lib.api.parsedate import parse_http_date_safe, parse_http_date
49 04a1b675 Christos Stavrakakis
from snf_django.lib.astakos import user_for_token
50 65bbcd43 Christos Stavrakakis
from snf_django.lib import api
51 65bbcd43 Christos Stavrakakis
from snf_django.lib.api import faults, utils
52 5a96180b Antony Chazapis
53 a7dff008 Antony Chazapis
from pithos.api.settings import (BACKEND_DB_MODULE, BACKEND_DB_CONNECTION,
54 761c2b3c root
                                 BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH,
55 761c2b3c root
                                 BACKEND_BLOCK_UMASK,
56 f4fbb0fa Sofia Papagiannaki
                                 BACKEND_QUEUE_MODULE, BACKEND_QUEUE_HOSTS,
57 1f3f907f Sofia Papagiannaki
                                 BACKEND_QUEUE_EXCHANGE, USE_QUOTAHOLDER,
58 3173699c Sofia Papagiannaki
                                 QUOTAHOLDER_URL, QUOTAHOLDER_TOKEN,
59 b336e6fa Georgios D. Tsoukalas
                                 QUOTAHOLDER_POOLSIZE,
60 6dd0fc7c Christos Stavrakakis
                                 ASTAKOS_URL,
61 3173699c Sofia Papagiannaki
                                 BACKEND_QUOTA, BACKEND_VERSIONING,
62 61c5b615 Sofia Papagiannaki
                                 BACKEND_FREE_VERSIONING, BACKEND_POOL_SIZE,
63 27932481 Sofia Papagiannaki
                                 COOKIE_NAME, USER_CATALOG_URL,
64 47462eda Filippos Giannakos
                                 RADOS_STORAGE, RADOS_POOL_BLOCKS,
65 56f3c759 Sofia Papagiannaki
                                 RADOS_POOL_MAPS, TRANSLATE_UUIDS,
66 4a105ce2 Sofia Papagiannaki
                                 PUBLIC_URL_SECURITY,
67 56f3c759 Sofia Papagiannaki
                                 PUBLIC_URL_ALPHABET)
68 32454501 Sofia Papagiannaki
from pithos.backends.base import (NotAllowedError, QuotaError, ItemNotExists,
69 32454501 Sofia Papagiannaki
                                  VersionNotExists)
70 04a1b675 Christos Stavrakakis
from snf_django.lib.astakos import (get_user_uuid, get_displayname,
71 890c2065 Sofia Papagiannaki
                                 get_uuids, get_displaynames)
72 b956618e Antony Chazapis
73 b956618e Antony Chazapis
import logging
74 22dab079 Antony Chazapis
import re
75 cbfb6636 Sofia Papagiannaki
import hashlib
76 7bef5750 Antony Chazapis
import uuid
77 c48acbfd Sofia Papagiannaki
import decimal
78 8c793655 Antony Chazapis
79 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
80 b956618e Antony Chazapis
81 b956618e Antony Chazapis
82 c48acbfd Sofia Papagiannaki
def json_encode_decimal(obj):
83 c48acbfd Sofia Papagiannaki
    if isinstance(obj, decimal.Decimal):
84 c48acbfd Sofia Papagiannaki
        return str(obj)
85 c48acbfd Sofia Papagiannaki
    raise TypeError(repr(obj) + " is not JSON serializable")
86 c48acbfd Sofia Papagiannaki
87 2715ade4 Sofia Papagiannaki
88 804e8fe7 Antony Chazapis
def rename_meta_key(d, old, new):
89 804e8fe7 Antony Chazapis
    if old not in d:
90 804e8fe7 Antony Chazapis
        return
91 804e8fe7 Antony Chazapis
    d[new] = d[old]
92 804e8fe7 Antony Chazapis
    del(d[old])
93 804e8fe7 Antony Chazapis
94 2715ade4 Sofia Papagiannaki
95 02c0c3fa Antony Chazapis
def printable_header_dict(d):
96 b956618e Antony Chazapis
    """Format a meta dictionary for printing out json/xml.
97 2715ade4 Sofia Papagiannaki

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

478 22dab079 Antony Chazapis
    Either returns None, when the header is not existent or should be ignored,
479 22dab079 Antony Chazapis
    or a list of (offset, length) tuples - should be further checked.
480 b956618e Antony Chazapis
    """
481 2715ade4 Sofia Papagiannaki
482 22dab079 Antony Chazapis
    ranges = request.META.get('HTTP_RANGE', '').replace(' ', '')
483 22dab079 Antony Chazapis
    if not ranges.startswith('bytes='):
484 b956618e Antony Chazapis
        return None
485 2715ade4 Sofia Papagiannaki
486 22dab079 Antony Chazapis
    ret = []
487 22dab079 Antony Chazapis
    for r in (x.strip() for x in ranges[6:].split(',')):
488 22dab079 Antony Chazapis
        p = re.compile('^(?P<offset>\d*)-(?P<upto>\d*)$')
489 22dab079 Antony Chazapis
        m = p.match(r)
490 22dab079 Antony Chazapis
        if not m:
491 22dab079 Antony Chazapis
            return None
492 22dab079 Antony Chazapis
        offset = m.group('offset')
493 22dab079 Antony Chazapis
        upto = m.group('upto')
494 22dab079 Antony Chazapis
        if offset == '' and upto == '':
495 b956618e Antony Chazapis
            return None
496 2715ade4 Sofia Papagiannaki
497 22dab079 Antony Chazapis
        if offset != '':
498 22dab079 Antony Chazapis
            offset = int(offset)
499 22dab079 Antony Chazapis
            if upto != '':
500 b956618e Antony Chazapis
                upto = int(upto)
501 22dab079 Antony Chazapis
                if offset > upto:
502 22dab079 Antony Chazapis
                    return None
503 22dab079 Antony Chazapis
                ret.append((offset, upto - offset + 1))
504 22dab079 Antony Chazapis
            else:
505 22dab079 Antony Chazapis
                ret.append((offset, size - offset))
506 b956618e Antony Chazapis
        else:
507 22dab079 Antony Chazapis
            length = int(upto)
508 22dab079 Antony Chazapis
            ret.append((size - length, length))
509 2715ade4 Sofia Papagiannaki
510 22dab079 Antony Chazapis
    return ret
511 22dab079 Antony Chazapis
512 2715ade4 Sofia Papagiannaki
513 22dab079 Antony Chazapis
def get_content_range(request):
514 58a6c894 Antony Chazapis
    """Parse a Content-Range header from the request.
515 2715ade4 Sofia Papagiannaki

516 22dab079 Antony Chazapis
    Either returns None, when the header is not existent or should be ignored,
517 22dab079 Antony Chazapis
    or an (offset, length, total) tuple - check as length, total may be None.
518 22dab079 Antony Chazapis
    Returns (None, None, None) if the provided range is '*/*'.
519 22dab079 Antony Chazapis
    """
520 2715ade4 Sofia Papagiannaki
521 22dab079 Antony Chazapis
    ranges = request.META.get('HTTP_CONTENT_RANGE', '')
522 22dab079 Antony Chazapis
    if not ranges:
523 22dab079 Antony Chazapis
        return None
524 2715ade4 Sofia Papagiannaki
525 22dab079 Antony Chazapis
    p = re.compile('^bytes (?P<offset>\d+)-(?P<upto>\d*)/(?P<total>(\d+|\*))$')
526 22dab079 Antony Chazapis
    m = p.match(ranges)
527 22dab079 Antony Chazapis
    if not m:
528 22dab079 Antony Chazapis
        if ranges == 'bytes */*':
529 22dab079 Antony Chazapis
            return (None, None, None)
530 22dab079 Antony Chazapis
        return None
531 22dab079 Antony Chazapis
    offset = int(m.group('offset'))
532 22dab079 Antony Chazapis
    upto = m.group('upto')
533 22dab079 Antony Chazapis
    total = m.group('total')
534 22dab079 Antony Chazapis
    if upto != '':
535 22dab079 Antony Chazapis
        upto = int(upto)
536 b956618e Antony Chazapis
    else:
537 22dab079 Antony Chazapis
        upto = None
538 22dab079 Antony Chazapis
    if total != '*':
539 22dab079 Antony Chazapis
        total = int(total)
540 22dab079 Antony Chazapis
    else:
541 22dab079 Antony Chazapis
        total = None
542 70e526a0 Antony Chazapis
    if (upto is not None and offset > upto) or \
543 70e526a0 Antony Chazapis
        (total is not None and offset >= total) or \
544 2715ade4 Sofia Papagiannaki
            (total is not None and upto is not None and upto >= total):
545 22dab079 Antony Chazapis
        return None
546 2715ade4 Sofia Papagiannaki
547 70e526a0 Antony Chazapis
    if upto is None:
548 22dab079 Antony Chazapis
        length = None
549 22dab079 Antony Chazapis
    else:
550 22dab079 Antony Chazapis
        length = upto - offset + 1
551 22dab079 Antony Chazapis
    return (offset, length, total)
552 b956618e Antony Chazapis
553 2715ade4 Sofia Papagiannaki
554 3436eeb0 Antony Chazapis
def get_sharing(request):
555 3436eeb0 Antony Chazapis
    """Parse an X-Object-Sharing header from the request.
556 2715ade4 Sofia Papagiannaki

557 3436eeb0 Antony Chazapis
    Raises BadRequest on error.
558 3436eeb0 Antony Chazapis
    """
559 2715ade4 Sofia Papagiannaki
560 3436eeb0 Antony Chazapis
    permissions = request.META.get('HTTP_X_OBJECT_SHARING')
561 cca6c617 Antony Chazapis
    if permissions is None:
562 3436eeb0 Antony Chazapis
        return None
563 2715ade4 Sofia Papagiannaki
564 0a7f1671 Antony Chazapis
    # TODO: Document or remove '~' replacing.
565 0a7f1671 Antony Chazapis
    permissions = permissions.replace('~', '')
566 2715ade4 Sofia Papagiannaki
567 3436eeb0 Antony Chazapis
    ret = {}
568 cca6c617 Antony Chazapis
    permissions = permissions.replace(' ', '')
569 cca6c617 Antony Chazapis
    if permissions == '':
570 cca6c617 Antony Chazapis
        return ret
571 cca6c617 Antony Chazapis
    for perm in (x for x in permissions.split(';')):
572 cca6c617 Antony Chazapis
        if perm.startswith('read='):
573 2715ade4 Sofia Papagiannaki
            ret['read'] = list(set(
574 2715ade4 Sofia Papagiannaki
                [v.replace(' ', '').lower() for v in perm[5:].split(',')]))
575 02c0c3fa Antony Chazapis
            if '' in ret['read']:
576 02c0c3fa Antony Chazapis
                ret['read'].remove('')
577 e8886082 Antony Chazapis
            if '*' in ret['read']:
578 e8886082 Antony Chazapis
                ret['read'] = ['*']
579 3436eeb0 Antony Chazapis
            if len(ret['read']) == 0:
580 bd40abfa Christos Stavrakakis
                raise faults.BadRequest(
581 32454501 Sofia Papagiannaki
                    'Bad X-Object-Sharing header value: invalid length')
582 3436eeb0 Antony Chazapis
        elif perm.startswith('write='):
583 2715ade4 Sofia Papagiannaki
            ret['write'] = list(set(
584 2715ade4 Sofia Papagiannaki
                [v.replace(' ', '').lower() for v in perm[6:].split(',')]))
585 02c0c3fa Antony Chazapis
            if '' in ret['write']:
586 02c0c3fa Antony Chazapis
                ret['write'].remove('')
587 e8886082 Antony Chazapis
            if '*' in ret['write']:
588 e8886082 Antony Chazapis
                ret['write'] = ['*']
589 3436eeb0 Antony Chazapis
            if len(ret['write']) == 0:
590 bd40abfa Christos Stavrakakis
                raise faults.BadRequest(
591 32454501 Sofia Papagiannaki
                    'Bad X-Object-Sharing header value: invalid length')
592 3436eeb0 Antony Chazapis
        else:
593 bd40abfa Christos Stavrakakis
            raise faults.BadRequest(
594 32454501 Sofia Papagiannaki
                'Bad X-Object-Sharing header value: missing prefix')
595 32454501 Sofia Papagiannaki
596 890c2065 Sofia Papagiannaki
    # replace displayname with uuid
597 469d0997 Georgios D. Tsoukalas
    if TRANSLATE_UUIDS:
598 469d0997 Georgios D. Tsoukalas
        try:
599 469d0997 Georgios D. Tsoukalas
            ret['read'] = [replace_permissions_displayname(
600 469d0997 Georgios D. Tsoukalas
                    getattr(request, 'token', None), x) \
601 469d0997 Georgios D. Tsoukalas
                        for x in ret.get('read', [])]
602 469d0997 Georgios D. Tsoukalas
            ret['write'] = [replace_permissions_displayname(
603 469d0997 Georgios D. Tsoukalas
                    getattr(request, 'token', None), x) \
604 469d0997 Georgios D. Tsoukalas
                        for x in ret.get('write', [])]
605 469d0997 Georgios D. Tsoukalas
        except ItemNotExists, e:
606 bd40abfa Christos Stavrakakis
            raise faults.BadRequest(
607 469d0997 Georgios D. Tsoukalas
                'Bad X-Object-Sharing header value: unknown account: %s' % e)
608 2715ade4 Sofia Papagiannaki
609 21a8a6ff Antony Chazapis
    # Keep duplicates only in write list.
610 2715ade4 Sofia Papagiannaki
    dups = [x for x in ret.get(
611 2715ade4 Sofia Papagiannaki
        'read', []) if x in ret.get('write', []) and x != '*']
612 21a8a6ff Antony Chazapis
    if dups:
613 21a8a6ff Antony Chazapis
        for x in dups:
614 21a8a6ff Antony Chazapis
            ret['read'].remove(x)
615 21a8a6ff Antony Chazapis
        if len(ret['read']) == 0:
616 21a8a6ff Antony Chazapis
            del(ret['read'])
617 2715ade4 Sofia Papagiannaki
618 3436eeb0 Antony Chazapis
    return ret
619 3436eeb0 Antony Chazapis
620 2715ade4 Sofia Papagiannaki
621 3ab38c43 Antony Chazapis
def get_public(request):
622 3ab38c43 Antony Chazapis
    """Parse an X-Object-Public header from the request.
623 2715ade4 Sofia Papagiannaki

624 3ab38c43 Antony Chazapis
    Raises BadRequest on error.
625 3ab38c43 Antony Chazapis
    """
626 2715ade4 Sofia Papagiannaki
627 3ab38c43 Antony Chazapis
    public = request.META.get('HTTP_X_OBJECT_PUBLIC')
628 3ab38c43 Antony Chazapis
    if public is None:
629 3ab38c43 Antony Chazapis
        return None
630 2715ade4 Sofia Papagiannaki
631 3ab38c43 Antony Chazapis
    public = public.replace(' ', '').lower()
632 3ab38c43 Antony Chazapis
    if public == 'true':
633 3ab38c43 Antony Chazapis
        return True
634 3ab38c43 Antony Chazapis
    elif public == 'false' or public == '':
635 3ab38c43 Antony Chazapis
        return False
636 bd40abfa Christos Stavrakakis
    raise faults.BadRequest('Bad X-Object-Public header value')
637 3ab38c43 Antony Chazapis
638 2715ade4 Sofia Papagiannaki
639 b956618e Antony Chazapis
def raw_input_socket(request):
640 58a6c894 Antony Chazapis
    """Return the socket for reading the rest of the request."""
641 2715ade4 Sofia Papagiannaki
642 b956618e Antony Chazapis
    server_software = request.META.get('SERVER_SOFTWARE')
643 fc1b2a75 Antony Chazapis
    if server_software and server_software.startswith('mod_python'):
644 b956618e Antony Chazapis
        return request._req
645 fc1b2a75 Antony Chazapis
    if 'wsgi.input' in request.environ:
646 fc1b2a75 Antony Chazapis
        return request.environ['wsgi.input']
647 08de868d Antony Chazapis
    raise NotImplemented('Unknown server software')
648 b956618e Antony Chazapis
649 2715ade4 Sofia Papagiannaki
MAX_UPLOAD_SIZE = 5 * (1024 * 1024 * 1024)  # 5GB
650 2715ade4 Sofia Papagiannaki
651 b956618e Antony Chazapis
652 c032f34d Antony Chazapis
def socket_read_iterator(request, length=0, blocksize=4096):
653 58a6c894 Antony Chazapis
    """Return a maximum of blocksize data read from the socket in each iteration.
654 2715ade4 Sofia Papagiannaki

655 22dab079 Antony Chazapis
    Read up to 'length'. If 'length' is negative, will attempt a chunked read.
656 b956618e Antony Chazapis
    The maximum ammount of data read is controlled by MAX_UPLOAD_SIZE.
657 b956618e Antony Chazapis
    """
658 2715ade4 Sofia Papagiannaki
659 c032f34d Antony Chazapis
    sock = raw_input_socket(request)
660 2715ade4 Sofia Papagiannaki
    if length < 0:  # Chunked transfers
661 c032f34d Antony Chazapis
        # Small version (server does the dechunking).
662 032dc768 Antony Chazapis
        if request.environ.get('mod_wsgi.input_chunked', None) or request.META['SERVER_SOFTWARE'].startswith('gunicorn'):
663 c032f34d Antony Chazapis
            while length < MAX_UPLOAD_SIZE:
664 c032f34d Antony Chazapis
                data = sock.read(blocksize)
665 c032f34d Antony Chazapis
                if data == '':
666 c032f34d Antony Chazapis
                    return
667 c032f34d Antony Chazapis
                yield data
668 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Maximum size is reached')
669 2715ade4 Sofia Papagiannaki
670 c032f34d Antony Chazapis
        # Long version (do the dechunking).
671 22dab079 Antony Chazapis
        data = ''
672 b956618e Antony Chazapis
        while length < MAX_UPLOAD_SIZE:
673 22dab079 Antony Chazapis
            # Get chunk size.
674 22dab079 Antony Chazapis
            if hasattr(sock, 'readline'):
675 22dab079 Antony Chazapis
                chunk_length = sock.readline()
676 22dab079 Antony Chazapis
            else:
677 22dab079 Antony Chazapis
                chunk_length = ''
678 22dab079 Antony Chazapis
                while chunk_length[-1:] != '\n':
679 22dab079 Antony Chazapis
                    chunk_length += sock.read(1)
680 22dab079 Antony Chazapis
                chunk_length.strip()
681 b956618e Antony Chazapis
            pos = chunk_length.find(';')
682 b956618e Antony Chazapis
            if pos >= 0:
683 b956618e Antony Chazapis
                chunk_length = chunk_length[:pos]
684 b956618e Antony Chazapis
            try:
685 b956618e Antony Chazapis
                chunk_length = int(chunk_length, 16)
686 bd40abfa Christos Stavrakakis
            except Exception:
687 bd40abfa Christos Stavrakakis
                raise faults.BadRequest('Bad chunk size')
688 2715ade4 Sofia Papagiannaki
                                 # TODO: Change to something more appropriate.
689 22dab079 Antony Chazapis
            # Check if done.
690 b956618e Antony Chazapis
            if chunk_length == 0:
691 22dab079 Antony Chazapis
                if len(data) > 0:
692 22dab079 Antony Chazapis
                    yield data
693 b956618e Antony Chazapis
                return
694 22dab079 Antony Chazapis
            # Get the actual data.
695 b956618e Antony Chazapis
            while chunk_length > 0:
696 22dab079 Antony Chazapis
                chunk = sock.read(min(chunk_length, blocksize))
697 22dab079 Antony Chazapis
                chunk_length -= len(chunk)
698 623a0cf4 Sofia Papagiannaki
                if length > 0:
699 623a0cf4 Sofia Papagiannaki
                    length += len(chunk)
700 22dab079 Antony Chazapis
                data += chunk
701 22dab079 Antony Chazapis
                if len(data) >= blocksize:
702 22dab079 Antony Chazapis
                    ret = data[:blocksize]
703 22dab079 Antony Chazapis
                    data = data[blocksize:]
704 22dab079 Antony Chazapis
                    yield ret
705 2715ade4 Sofia Papagiannaki
            sock.read(2)  # CRLF
706 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Maximum size is reached')
707 b956618e Antony Chazapis
    else:
708 b956618e Antony Chazapis
        if length > MAX_UPLOAD_SIZE:
709 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Maximum size is reached')
710 b956618e Antony Chazapis
        while length > 0:
711 b956618e Antony Chazapis
            data = sock.read(min(length, blocksize))
712 7b25e082 Antony Chazapis
            if not data:
713 bd40abfa Christos Stavrakakis
                raise faults.BadRequest()
714 b956618e Antony Chazapis
            length -= len(data)
715 b956618e Antony Chazapis
            yield data
716 b956618e Antony Chazapis
717 2715ade4 Sofia Papagiannaki
718 817890f2 Antony Chazapis
class SaveToBackendHandler(FileUploadHandler):
719 817890f2 Antony Chazapis
    """Handle a file from an HTML form the django way."""
720 2715ade4 Sofia Papagiannaki
721 817890f2 Antony Chazapis
    def __init__(self, request=None):
722 817890f2 Antony Chazapis
        super(SaveToBackendHandler, self).__init__(request)
723 817890f2 Antony Chazapis
        self.backend = request.backend
724 2715ade4 Sofia Papagiannaki
725 817890f2 Antony Chazapis
    def put_data(self, length):
726 817890f2 Antony Chazapis
        if len(self.data) >= length:
727 817890f2 Antony Chazapis
            block = self.data[:length]
728 817890f2 Antony Chazapis
            self.file.hashmap.append(self.backend.put_block(block))
729 817890f2 Antony Chazapis
            self.md5.update(block)
730 817890f2 Antony Chazapis
            self.data = self.data[length:]
731 2715ade4 Sofia Papagiannaki
732 817890f2 Antony Chazapis
    def new_file(self, field_name, file_name, content_type, content_length, charset=None):
733 2715ade4 Sofia Papagiannaki
        self.md5 = hashlib.md5()
734 817890f2 Antony Chazapis
        self.data = ''
735 2715ade4 Sofia Papagiannaki
        self.file = UploadedFile(
736 2715ade4 Sofia Papagiannaki
            name=file_name, content_type=content_type, charset=charset)
737 817890f2 Antony Chazapis
        self.file.size = 0
738 817890f2 Antony Chazapis
        self.file.hashmap = []
739 2715ade4 Sofia Papagiannaki
740 817890f2 Antony Chazapis
    def receive_data_chunk(self, raw_data, start):
741 817890f2 Antony Chazapis
        self.data += raw_data
742 817890f2 Antony Chazapis
        self.file.size += len(raw_data)
743 817890f2 Antony Chazapis
        self.put_data(self.request.backend.block_size)
744 817890f2 Antony Chazapis
        return None
745 2715ade4 Sofia Papagiannaki
746 817890f2 Antony Chazapis
    def file_complete(self, file_size):
747 817890f2 Antony Chazapis
        l = len(self.data)
748 817890f2 Antony Chazapis
        if l > 0:
749 817890f2 Antony Chazapis
            self.put_data(l)
750 817890f2 Antony Chazapis
        self.file.etag = self.md5.hexdigest().lower()
751 817890f2 Antony Chazapis
        return self.file
752 817890f2 Antony Chazapis
753 2715ade4 Sofia Papagiannaki
754 22dab079 Antony Chazapis
class ObjectWrapper(object):
755 58a6c894 Antony Chazapis
    """Return the object's data block-per-block in each iteration.
756 2715ade4 Sofia Papagiannaki

757 22dab079 Antony Chazapis
    Read from the object using the offset and length provided in each entry of the range list.
758 22dab079 Antony Chazapis
    """
759 2715ade4 Sofia Papagiannaki
760 39593b2b Giorgos Verigakis
    def __init__(self, backend, ranges, sizes, hashmaps, boundary):
761 39593b2b Giorgos Verigakis
        self.backend = backend
762 22dab079 Antony Chazapis
        self.ranges = ranges
763 8cb45c13 Antony Chazapis
        self.sizes = sizes
764 8cb45c13 Antony Chazapis
        self.hashmaps = hashmaps
765 22dab079 Antony Chazapis
        self.boundary = boundary
766 8cb45c13 Antony Chazapis
        self.size = sum(self.sizes)
767 2715ade4 Sofia Papagiannaki
768 8cb45c13 Antony Chazapis
        self.file_index = 0
769 8cb45c13 Antony Chazapis
        self.block_index = 0
770 8cb45c13 Antony Chazapis
        self.block_hash = -1
771 22dab079 Antony Chazapis
        self.block = ''
772 2715ade4 Sofia Papagiannaki
773 22dab079 Antony Chazapis
        self.range_index = -1
774 22dab079 Antony Chazapis
        self.offset, self.length = self.ranges[0]
775 2715ade4 Sofia Papagiannaki
776 22dab079 Antony Chazapis
    def __iter__(self):
777 22dab079 Antony Chazapis
        return self
778 2715ade4 Sofia Papagiannaki
779 22dab079 Antony Chazapis
    def part_iterator(self):
780 22dab079 Antony Chazapis
        if self.length > 0:
781 8cb45c13 Antony Chazapis
            # Get the file for the current offset.
782 8cb45c13 Antony Chazapis
            file_size = self.sizes[self.file_index]
783 8cb45c13 Antony Chazapis
            while self.offset >= file_size:
784 8cb45c13 Antony Chazapis
                self.offset -= file_size
785 8cb45c13 Antony Chazapis
                self.file_index += 1
786 8cb45c13 Antony Chazapis
                file_size = self.sizes[self.file_index]
787 2715ade4 Sofia Papagiannaki
788 8cb45c13 Antony Chazapis
            # Get the block for the current position.
789 39593b2b Giorgos Verigakis
            self.block_index = int(self.offset / self.backend.block_size)
790 8cb45c13 Antony Chazapis
            if self.block_hash != self.hashmaps[self.file_index][self.block_index]:
791 2715ade4 Sofia Papagiannaki
                self.block_hash = self.hashmaps[
792 2715ade4 Sofia Papagiannaki
                    self.file_index][self.block_index]
793 22dab079 Antony Chazapis
                try:
794 39593b2b Giorgos Verigakis
                    self.block = self.backend.get_block(self.block_hash)
795 7efc9f86 Sofia Papagiannaki
                except ItemNotExists:
796 bd40abfa Christos Stavrakakis
                    raise faults.ItemNotFound('Block does not exist')
797 2715ade4 Sofia Papagiannaki
798 22dab079 Antony Chazapis
            # Get the data from the block.
799 39593b2b Giorgos Verigakis
            bo = self.offset % self.backend.block_size
800 c9865fe1 Antony Chazapis
            bs = self.backend.block_size
801 45cf0bc8 Antony Chazapis
            if (self.block_index == len(self.hashmaps[self.file_index]) - 1 and
802 2715ade4 Sofia Papagiannaki
                    self.sizes[self.file_index] % self.backend.block_size):
803 c9865fe1 Antony Chazapis
                bs = self.sizes[self.file_index] % self.backend.block_size
804 c9865fe1 Antony Chazapis
            bl = min(self.length, bs - bo)
805 22dab079 Antony Chazapis
            data = self.block[bo:bo + bl]
806 22dab079 Antony Chazapis
            self.offset += bl
807 22dab079 Antony Chazapis
            self.length -= bl
808 22dab079 Antony Chazapis
            return data
809 22dab079 Antony Chazapis
        else:
810 22dab079 Antony Chazapis
            raise StopIteration
811 2715ade4 Sofia Papagiannaki
812 22dab079 Antony Chazapis
    def next(self):
813 22dab079 Antony Chazapis
        if len(self.ranges) == 1:
814 22dab079 Antony Chazapis
            return self.part_iterator()
815 22dab079 Antony Chazapis
        if self.range_index == len(self.ranges):
816 22dab079 Antony Chazapis
            raise StopIteration
817 22dab079 Antony Chazapis
        try:
818 22dab079 Antony Chazapis
            if self.range_index == -1:
819 22dab079 Antony Chazapis
                raise StopIteration
820 22dab079 Antony Chazapis
            return self.part_iterator()
821 22dab079 Antony Chazapis
        except StopIteration:
822 22dab079 Antony Chazapis
            self.range_index += 1
823 22dab079 Antony Chazapis
            out = []
824 22dab079 Antony Chazapis
            if self.range_index < len(self.ranges):
825 22dab079 Antony Chazapis
                # Part header.
826 22dab079 Antony Chazapis
                self.offset, self.length = self.ranges[self.range_index]
827 8cb45c13 Antony Chazapis
                self.file_index = 0
828 22dab079 Antony Chazapis
                if self.range_index > 0:
829 22dab079 Antony Chazapis
                    out.append('')
830 22dab079 Antony Chazapis
                out.append('--' + self.boundary)
831 2715ade4 Sofia Papagiannaki
                out.append('Content-Range: bytes %d-%d/%d' % (
832 2715ade4 Sofia Papagiannaki
                    self.offset, self.offset + self.length - 1, self.size))
833 22dab079 Antony Chazapis
                out.append('Content-Transfer-Encoding: binary')
834 22dab079 Antony Chazapis
                out.append('')
835 22dab079 Antony Chazapis
                out.append('')
836 22dab079 Antony Chazapis
                return '\r\n'.join(out)
837 22dab079 Antony Chazapis
            else:
838 22dab079 Antony Chazapis
                # Footer.
839 22dab079 Antony Chazapis
                out.append('')
840 22dab079 Antony Chazapis
                out.append('--' + self.boundary + '--')
841 22dab079 Antony Chazapis
                out.append('')
842 22dab079 Antony Chazapis
                return '\r\n'.join(out)
843 22dab079 Antony Chazapis
844 2715ade4 Sofia Papagiannaki
845 8cb45c13 Antony Chazapis
def object_data_response(request, sizes, hashmaps, meta, public=False):
846 7bef5750 Antony Chazapis
    """Get the HttpResponse object for replying with the object's data."""
847 2715ade4 Sofia Papagiannaki
848 7bef5750 Antony Chazapis
    # Range handling.
849 8cb45c13 Antony Chazapis
    size = sum(sizes)
850 7bef5750 Antony Chazapis
    ranges = get_range(request, size)
851 7bef5750 Antony Chazapis
    if ranges is None:
852 7bef5750 Antony Chazapis
        ranges = [(0, size)]
853 7bef5750 Antony Chazapis
        ret = 200
854 7bef5750 Antony Chazapis
    else:
855 7bef5750 Antony Chazapis
        check = [True for offset, length in ranges if
856 2715ade4 Sofia Papagiannaki
                 length <= 0 or length > size or
857 2715ade4 Sofia Papagiannaki
                 offset < 0 or offset >= size or
858 2715ade4 Sofia Papagiannaki
                 offset + length > size]
859 7bef5750 Antony Chazapis
        if len(check) > 0:
860 bd40abfa Christos Stavrakakis
            raise faults.RangeNotSatisfiable('Requested range exceeds object limits')
861 7bef5750 Antony Chazapis
        ret = 206
862 15d465b8 Antony Chazapis
        if_range = request.META.get('HTTP_IF_RANGE')
863 15d465b8 Antony Chazapis
        if if_range:
864 6d1e6dce Sofia Papagiannaki
            try:
865 15d465b8 Antony Chazapis
                # Modification time has passed instead.
866 6d1e6dce Sofia Papagiannaki
                last_modified = parse_http_date(if_range)
867 6d1e6dce Sofia Papagiannaki
                if last_modified != meta['modified']:
868 6d1e6dce Sofia Papagiannaki
                    ranges = [(0, size)]
869 6d1e6dce Sofia Papagiannaki
                    ret = 200
870 6d1e6dce Sofia Papagiannaki
            except ValueError:
871 33b4e4a6 Antony Chazapis
                if if_range != meta['checksum']:
872 6d1e6dce Sofia Papagiannaki
                    ranges = [(0, size)]
873 6d1e6dce Sofia Papagiannaki
                    ret = 200
874 2715ade4 Sofia Papagiannaki
875 7bef5750 Antony Chazapis
    if ret == 206 and len(ranges) > 1:
876 7bef5750 Antony Chazapis
        boundary = uuid.uuid4().hex
877 7bef5750 Antony Chazapis
    else:
878 7bef5750 Antony Chazapis
        boundary = ''
879 39593b2b Giorgos Verigakis
    wrapper = ObjectWrapper(request.backend, ranges, sizes, hashmaps, boundary)
880 7bef5750 Antony Chazapis
    response = HttpResponse(wrapper, status=ret)
881 469d0997 Georgios D. Tsoukalas
    put_object_headers(
882 469d0997 Georgios D. Tsoukalas
            response, meta, restricted=public, token=getattr(request, 'token', None))
883 7bef5750 Antony Chazapis
    if ret == 206:
884 7bef5750 Antony Chazapis
        if len(ranges) == 1:
885 7bef5750 Antony Chazapis
            offset, length = ranges[0]
886 2715ade4 Sofia Papagiannaki
            response[
887 2715ade4 Sofia Papagiannaki
                'Content-Length'] = length  # Update with the correct length.
888 2715ade4 Sofia Papagiannaki
            response['Content-Range'] = 'bytes %d-%d/%d' % (
889 2715ade4 Sofia Papagiannaki
                offset, offset + length - 1, size)
890 7bef5750 Antony Chazapis
        else:
891 7bef5750 Antony Chazapis
            del(response['Content-Length'])
892 2715ade4 Sofia Papagiannaki
            response['Content-Type'] = 'multipart/byteranges; boundary=%s' % (
893 2715ade4 Sofia Papagiannaki
                boundary,)
894 7bef5750 Antony Chazapis
    return response
895 7bef5750 Antony Chazapis
896 2715ade4 Sofia Papagiannaki
897 39593b2b Giorgos Verigakis
def put_object_block(request, hashmap, data, offset):
898 cb146cf9 Antony Chazapis
    """Put one block of data at the given offset."""
899 2715ade4 Sofia Papagiannaki
900 39593b2b Giorgos Verigakis
    bi = int(offset / request.backend.block_size)
901 39593b2b Giorgos Verigakis
    bo = offset % request.backend.block_size
902 39593b2b Giorgos Verigakis
    bl = min(len(data), request.backend.block_size - bo)
903 cb146cf9 Antony Chazapis
    if bi < len(hashmap):
904 39593b2b Giorgos Verigakis
        hashmap[bi] = request.backend.update_block(hashmap[bi], data[:bl], bo)
905 cb146cf9 Antony Chazapis
    else:
906 39593b2b Giorgos Verigakis
        hashmap.append(request.backend.put_block(('\x00' * bo) + data[:bl]))
907 2715ade4 Sofia Papagiannaki
    return bl  # Return ammount of data written.
908 2715ade4 Sofia Papagiannaki
909 cb146cf9 Antony Chazapis
910 b3155065 Antony Chazapis
def hashmap_md5(backend, hashmap, size):
911 cddcf432 chazapis
    """Produce the MD5 sum from the data in the hashmap."""
912 2715ade4 Sofia Papagiannaki
913 cddcf432 chazapis
    # TODO: Search backend for the MD5 of another object with the same hashmap and size...
914 cddcf432 chazapis
    md5 = hashlib.md5()
915 b3155065 Antony Chazapis
    bs = backend.block_size
916 cddcf432 chazapis
    for bi, hash in enumerate(hashmap):
917 2715ade4 Sofia Papagiannaki
        data = backend.get_block(hash)  # Blocks come in padded.
918 cddcf432 chazapis
        if bi == len(hashmap) - 1:
919 c9865fe1 Antony Chazapis
            data = data[:size % bs]
920 c9865fe1 Antony Chazapis
        md5.update(data)
921 cddcf432 chazapis
    return md5.hexdigest().lower()
922 49133392 Antony Chazapis
923 2715ade4 Sofia Papagiannaki
924 af7bb62f Antony Chazapis
def simple_list_response(request, l):
925 6b6b6c1e Antony Chazapis
    if request.serialization == 'text':
926 6b6b6c1e Antony Chazapis
        return '\n'.join(l) + '\n'
927 6b6b6c1e Antony Chazapis
    if request.serialization == 'xml':
928 af7bb62f Antony Chazapis
        return render_to_string('items.xml', {'items': l})
929 6b6b6c1e Antony Chazapis
    if request.serialization == 'json':
930 6b6b6c1e Antony Chazapis
        return json.dumps(l)
931 6b6b6c1e Antony Chazapis
932 35d42381 Vangelis Koukis
933 edb7875c Christos Stavrakakis
from pithos.backends.util import PithosBackendPool
934 61c5b615 Sofia Papagiannaki
935 47462eda Filippos Giannakos
if RADOS_STORAGE:
936 47462eda Filippos Giannakos
    BLOCK_PARAMS = { 'mappool': RADOS_POOL_MAPS,
937 47462eda Filippos Giannakos
                     'blockpool': RADOS_POOL_BLOCKS,
938 47462eda Filippos Giannakos
                   }
939 47462eda Filippos Giannakos
else:
940 47462eda Filippos Giannakos
    BLOCK_PARAMS = { 'mappool': None,
941 47462eda Filippos Giannakos
                     'blockpool': None,
942 47462eda Filippos Giannakos
                   }
943 35d42381 Vangelis Koukis
944 761c2b3c root
945 b336e6fa Georgios D. Tsoukalas
_pithos_backend_pool = PithosBackendPool(
946 61c5b615 Sofia Papagiannaki
        size=BACKEND_POOL_SIZE,
947 b336e6fa Georgios D. Tsoukalas
        db_module=BACKEND_DB_MODULE,
948 b336e6fa Georgios D. Tsoukalas
        db_connection=BACKEND_DB_CONNECTION,
949 b336e6fa Georgios D. Tsoukalas
        block_module=BACKEND_BLOCK_MODULE,
950 b336e6fa Georgios D. Tsoukalas
        block_path=BACKEND_BLOCK_PATH,
951 b336e6fa Georgios D. Tsoukalas
        block_umask=BACKEND_BLOCK_UMASK,
952 b336e6fa Georgios D. Tsoukalas
        queue_module=BACKEND_QUEUE_MODULE,
953 b336e6fa Georgios D. Tsoukalas
        queue_hosts=BACKEND_QUEUE_HOSTS,
954 b336e6fa Georgios D. Tsoukalas
        queue_exchange=BACKEND_QUEUE_EXCHANGE,
955 b336e6fa Georgios D. Tsoukalas
        quotaholder_enabled=USE_QUOTAHOLDER,
956 b336e6fa Georgios D. Tsoukalas
        quotaholder_url=QUOTAHOLDER_URL,
957 b336e6fa Georgios D. Tsoukalas
        quotaholder_token=QUOTAHOLDER_TOKEN,
958 b336e6fa Georgios D. Tsoukalas
        quotaholder_client_poolsize=QUOTAHOLDER_POOLSIZE,
959 b336e6fa Georgios D. Tsoukalas
        free_versioning=BACKEND_FREE_VERSIONING,
960 56f3c759 Sofia Papagiannaki
        block_params=BLOCK_PARAMS,
961 4a105ce2 Sofia Papagiannaki
        public_url_security=PUBLIC_URL_SECURITY,
962 56f3c759 Sofia Papagiannaki
        public_url_alphabet=PUBLIC_URL_ALPHABET)
963 35d42381 Vangelis Koukis
964 65bbcd43 Christos Stavrakakis
965 35d42381 Vangelis Koukis
def get_backend():
966 edb7875c Christos Stavrakakis
    backend = _pithos_backend_pool.pool_get()
967 edb7875c Christos Stavrakakis
    backend.default_policy['quota'] = BACKEND_QUOTA
968 edb7875c Christos Stavrakakis
    backend.default_policy['versioning'] = BACKEND_VERSIONING
969 f77b1da9 Stratos Psomadakis
    backend.messages = []
970 edb7875c Christos Stavrakakis
    return backend
971 35d42381 Vangelis Koukis
972 35d42381 Vangelis Koukis
973 9fefc052 Antony Chazapis
def update_request_headers(request):
974 9fefc052 Antony Chazapis
    # Handle URL-encoded keys and values.
975 2715ade4 Sofia Papagiannaki
    meta = dict([(
976 2715ade4 Sofia Papagiannaki
        k, v) for k, v in request.META.iteritems() if k.startswith('HTTP_')])
977 88283e9e Antony Chazapis
    for k, v in meta.iteritems():
978 88283e9e Antony Chazapis
        try:
979 88283e9e Antony Chazapis
            k.decode('ascii')
980 88283e9e Antony Chazapis
            v.decode('ascii')
981 88283e9e Antony Chazapis
        except UnicodeDecodeError:
982 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Bad character in headers.')
983 88283e9e Antony Chazapis
        if '%' in k or '%' in v:
984 88283e9e Antony Chazapis
            del(request.META[k])
985 2715ade4 Sofia Papagiannaki
            request.META[unquote(k)] = smart_unicode(unquote(
986 2715ade4 Sofia Papagiannaki
                v), strings_only=True)
987 2715ade4 Sofia Papagiannaki
988 9fefc052 Antony Chazapis
989 b956618e Antony Chazapis
def update_response_headers(request, response):
990 9fefc052 Antony Chazapis
    if (not response.has_header('Content-Length') and
991 9fefc052 Antony Chazapis
        not (response.has_header('Content-Type') and
992 9fefc052 Antony Chazapis
             response['Content-Type'].startswith('multipart/byteranges'))):
993 b980169d Antony Chazapis
        response['Content-Length'] = len(response.content)
994 2715ade4 Sofia Papagiannaki
995 9fefc052 Antony Chazapis
    # URL-encode unicode in headers.
996 9fefc052 Antony Chazapis
    meta = response.items()
997 9fefc052 Antony Chazapis
    for k, v in meta:
998 88283e9e Antony Chazapis
        if (k.startswith('X-Account-') or k.startswith('X-Container-') or
999 2715ade4 Sofia Papagiannaki
                k.startswith('X-Object-') or k.startswith('Content-')):
1000 9fefc052 Antony Chazapis
            del(response[k])
1001 88283e9e Antony Chazapis
            response[quote(k)] = quote(v, safe='/=,:@; ')
1002 b956618e Antony Chazapis
1003 2715ade4 Sofia Papagiannaki
1004 65bbcd43 Christos Stavrakakis
def get_pithos_usage(token):
1005 65bbcd43 Christos Stavrakakis
    """Get Pithos Usage from astakos."""
1006 6dd0fc7c Christos Stavrakakis
    astakos_url = ASTAKOS_URL + "im/authenticate"
1007 4ab620b6 Christos Stavrakakis
    user_info = user_for_token(token, astakos_url, usage=True)
1008 65bbcd43 Christos Stavrakakis
    usage = user_info.get("usage", [])
1009 c846fad1 Sofia Papagiannaki
    for u in usage:
1010 c846fad1 Sofia Papagiannaki
        if u.get('name') == 'pithos+.diskspace':
1011 c846fad1 Sofia Papagiannaki
            return u
1012 c846fad1 Sofia Papagiannaki
1013 2715ade4 Sofia Papagiannaki
1014 65bbcd43 Christos Stavrakakis
def api_method(http_method=None, user_required=True, logger=None,
1015 65bbcd43 Christos Stavrakakis
               format_allowed=False):
1016 b956618e Antony Chazapis
    def decorator(func):
1017 65bbcd43 Christos Stavrakakis
        @api.api_method(http_method=http_method, user_required=user_required,
1018 65bbcd43 Christos Stavrakakis
                          logger=logger, format_allowed=format_allowed)
1019 b956618e Antony Chazapis
        @wraps(func)
1020 b956618e Antony Chazapis
        def wrapper(request, *args, **kwargs):
1021 65bbcd43 Christos Stavrakakis
            # The args variable may contain up to (account, container, object).
1022 65bbcd43 Christos Stavrakakis
            if len(args) > 1 and len(args[1]) > 256:
1023 65bbcd43 Christos Stavrakakis
                raise faults.BadRequest("Container name too large")
1024 65bbcd43 Christos Stavrakakis
            if len(args) > 2 and len(args[2]) > 1024:
1025 65bbcd43 Christos Stavrakakis
                raise faults.BadRequest('Object name too large.')
1026 2715ade4 Sofia Papagiannaki
1027 65bbcd43 Christos Stavrakakis
            try:
1028 65bbcd43 Christos Stavrakakis
                # Add a PithosBackend as attribute of the request object
1029 228de81b Antony Chazapis
                request.backend = get_backend()
1030 65bbcd43 Christos Stavrakakis
                # Many API method expect thet X-Auth-Token in request,token
1031 65bbcd43 Christos Stavrakakis
                request.token = request.x_auth_token
1032 65bbcd43 Christos Stavrakakis
                update_request_headers(request)
1033 b956618e Antony Chazapis
                response = func(request, *args, **kwargs)
1034 b956618e Antony Chazapis
                update_response_headers(request, response)
1035 b956618e Antony Chazapis
                return response
1036 7b25e082 Antony Chazapis
            finally:
1037 65bbcd43 Christos Stavrakakis
                # Always close PithosBackend connection
1038 65bbcd43 Christos Stavrakakis
                if getattr(request, "backend", None) is not None:
1039 d14fe290 Antony Chazapis
                    request.backend.close()
1040 b956618e Antony Chazapis
        return wrapper
1041 b956618e Antony Chazapis
    return decorator