Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (38.1 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 b17e5550 Giorgos Korfiatis
                                 BACKEND_QUEUE_EXCHANGE,
58 b336e6fa Georgios D. Tsoukalas
                                 QUOTAHOLDER_POOLSIZE,
59 b17e5550 Giorgos Korfiatis
                                 SERVICE_TOKEN,
60 6dd0fc7c Christos Stavrakakis
                                 ASTAKOS_URL,
61 19ddd41b Sofia Papagiannaki
                                 BACKEND_ACCOUNT_QUOTA, BACKEND_CONTAINER_QUOTA,
62 19ddd41b Sofia Papagiannaki
                                 BACKEND_VERSIONING,
63 61c5b615 Sofia Papagiannaki
                                 BACKEND_FREE_VERSIONING, BACKEND_POOL_SIZE,
64 27932481 Sofia Papagiannaki
                                 COOKIE_NAME, USER_CATALOG_URL,
65 47462eda Filippos Giannakos
                                 RADOS_STORAGE, RADOS_POOL_BLOCKS,
66 56f3c759 Sofia Papagiannaki
                                 RADOS_POOL_MAPS, TRANSLATE_UUIDS,
67 4a105ce2 Sofia Papagiannaki
                                 PUBLIC_URL_SECURITY,
68 56f3c759 Sofia Papagiannaki
                                 PUBLIC_URL_ALPHABET)
69 32454501 Sofia Papagiannaki
from pithos.backends.base import (NotAllowedError, QuotaError, ItemNotExists,
70 32454501 Sofia Papagiannaki
                                  VersionNotExists)
71 04a1b675 Christos Stavrakakis
from snf_django.lib.astakos import (get_user_uuid, get_displayname,
72 890c2065 Sofia Papagiannaki
                                 get_uuids, get_displaynames)
73 b956618e Antony Chazapis
74 b956618e Antony Chazapis
import logging
75 22dab079 Antony Chazapis
import re
76 cbfb6636 Sofia Papagiannaki
import hashlib
77 7bef5750 Antony Chazapis
import uuid
78 c48acbfd Sofia Papagiannaki
import decimal
79 8c793655 Antony Chazapis
80 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
81 b956618e Antony Chazapis
82 b956618e Antony Chazapis
83 c48acbfd Sofia Papagiannaki
def json_encode_decimal(obj):
84 c48acbfd Sofia Papagiannaki
    if isinstance(obj, decimal.Decimal):
85 c48acbfd Sofia Papagiannaki
        return str(obj)
86 c48acbfd Sofia Papagiannaki
    raise TypeError(repr(obj) + " is not JSON serializable")
87 c48acbfd Sofia Papagiannaki
88 2715ade4 Sofia Papagiannaki
89 804e8fe7 Antony Chazapis
def rename_meta_key(d, old, new):
90 804e8fe7 Antony Chazapis
    if old not in d:
91 804e8fe7 Antony Chazapis
        return
92 804e8fe7 Antony Chazapis
    d[new] = d[old]
93 804e8fe7 Antony Chazapis
    del(d[old])
94 804e8fe7 Antony Chazapis
95 2715ade4 Sofia Papagiannaki
96 02c0c3fa Antony Chazapis
def printable_header_dict(d):
97 b956618e Antony Chazapis
    """Format a meta dictionary for printing out json/xml.
98 2715ade4 Sofia Papagiannaki

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

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

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

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

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

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

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