Revision d50ed8d4

b/other/migrate.py
1 1
#!/usr/bin/env python
2 2

  
3 3
# Copyright 2011-2012 GRNET S.A. All rights reserved.
4
# 
4
#
5 5
# Redistribution and use in source and binary forms, with or
6 6
# without modification, are permitted provided that the following
7 7
# conditions are met:
8
# 
8
#
9 9
#   1. Redistributions of source code must retain the above
10 10
#      copyright notice, this list of conditions and the following
11 11
#      disclaimer.
12
# 
12
#
13 13
#   2. Redistributions in binary form must reproduce the above
14 14
#      copyright notice, this list of conditions and the following
15 15
#      disclaimer in the documentation and/or other materials
16 16
#      provided with the distribution.
17
# 
17
#
18 18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
27 27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 29
# POSSIBILITY OF SUCH DAMAGE.
30
# 
30
#
31 31
# The views and conclusions contained in the software and
32 32
# documentation are those of the authors and should not be
33 33
# interpreted as representing official policies, either expressed
......
41 41

  
42 42
from pithos.backends.modular import ModularBackend
43 43

  
44

  
44 45
class Migration(object):
45 46
    def __init__(self, db):
46 47
        self.engine = create_engine(db)
47 48
        self.metadata = MetaData(self.engine)
48 49
        #self.engine.echo = True
49 50
        self.conn = self.engine.connect()
50
        
51

  
51 52
        options = getattr(settings, 'BACKEND', None)[1]
52 53
        self.backend = ModularBackend(*options)
53
    
54

  
54 55
    def execute(self):
55 56
        pass
56 57

  
58

  
57 59
class Cache():
58 60
    def __init__(self, db):
59 61
        self.engine = create_engine(db)
60 62
        metadata = MetaData(self.engine)
61
        
62
        columns=[]
63

  
64
        columns = []
63 65
        columns.append(Column('path', String(2048), primary_key=True))
64 66
        columns.append(Column('hash', String(255)))
65 67
        self.files = Table('files', metadata, *columns)
66 68
        self.conn = self.engine.connect()
67 69
        self.engine.echo = True
68 70
        metadata.create_all(self.engine)
69
    
71

  
70 72
    def put(self, path, hash):
71 73
        # Insert or replace.
72
        s = self.files.delete().where(self.files.c.path==path)
74
        s = self.files.delete().where(self.files.c.path == path)
73 75
        r = self.conn.execute(s)
74 76
        r.close()
75 77
        s = self.files.insert()
76 78
        r = self.conn.execute(s, {'path': path, 'hash': hash})
77 79
        r.close()
78
    
80

  
79 81
    def get(self, path):
80 82
        s = select([self.files.c.hash], self.files.c.path == path)
81 83
        r = self.conn.execute(s)
b/snf-pithos-app/distribute_setup.py
144 144
        except ImportError:
145 145
            return _do_download(version, download_base, to_dir, download_delay)
146 146
        try:
147
            pkg_resources.require("distribute>="+version)
147
            pkg_resources.require("distribute>=" + version)
148 148
            return
149 149
        except pkg_resources.VersionConflict:
150 150
            e = sys.exc_info()[1]
151 151
            if was_imported:
152 152
                sys.stderr.write(
153
                "The required version of distribute (>=%s) is not available,\n"
154
                "and can't be installed while this script is running. Please\n"
155
                "install a more recent version first, using\n"
156
                "'easy_install -U distribute'."
157
                "\n\n(Currently using %r)\n" % (version, e.args[0]))
153
                    "The required version of distribute (>=%s) is not available,\n"
154
                    "and can't be installed while this script is running. Please\n"
155
                    "install a more recent version first, using\n"
156
                    "'easy_install -U distribute'."
157
                    "\n\n(Currently using %r)\n" % (version, e.args[0]))
158 158
                sys.exit(2)
159 159
            else:
160 160
                del pkg_resources, sys.modules['pkg_resources']    # reload ok
......
167 167
        if not no_fake:
168 168
            _create_fake_setuptools_pkg_info(to_dir)
169 169

  
170

  
170 171
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
171 172
                        to_dir=os.curdir, delay=15):
172 173
    """Download distribute from a specified location and return its filename
......
203 204
                dst.close()
204 205
    return os.path.realpath(saveto)
205 206

  
207

  
206 208
def _no_sandbox(function):
207 209
    def __no_sandbox(*args, **kw):
208 210
        try:
......
227 229

  
228 230
    return __no_sandbox
229 231

  
232

  
230 233
def _patch_file(path, content):
231 234
    """Will backup the file then patch it"""
232 235
    existing_content = open(path).read()
......
245 248

  
246 249
_patch_file = _no_sandbox(_patch_file)
247 250

  
251

  
248 252
def _same_content(path, content):
249 253
    return open(path).read() == content
250 254

  
255

  
251 256
def _rename_path(path):
252 257
    new_name = path + '.OLD.%s' % time.time()
253 258
    log.warn('Renaming %s into %s', path, new_name)
254 259
    os.rename(path, new_name)
255 260
    return new_name
256 261

  
262

  
257 263
def _remove_flat_installation(placeholder):
258 264
    if not os.path.isdir(placeholder):
259 265
        log.warn('Unkown installation at %s', placeholder)
......
289 295

  
290 296
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
291 297

  
298

  
292 299
def _after_install(dist):
293 300
    log.warn('After install bootstrap.')
294 301
    placeholder = dist.get_command_obj('install').install_purelib
295 302
    _create_fake_setuptools_pkg_info(placeholder)
296 303

  
304

  
297 305
def _create_fake_setuptools_pkg_info(placeholder):
298 306
    if not placeholder or not os.path.exists(placeholder):
299 307
        log.warn('Could not find the install location')
300 308
        return
301 309
    pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
302 310
    setuptools_file = 'setuptools-%s-py%s.egg-info' % \
303
            (SETUPTOOLS_FAKED_VERSION, pyver)
311
        (SETUPTOOLS_FAKED_VERSION, pyver)
304 312
    pkg_info = os.path.join(placeholder, setuptools_file)
305 313
    if os.path.exists(pkg_info):
306 314
        log.warn('%s already exists', pkg_info)
......
321 329
    finally:
322 330
        f.close()
323 331

  
324
_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
332
_create_fake_setuptools_pkg_info = _no_sandbox(
333
    _create_fake_setuptools_pkg_info)
334

  
325 335

  
326 336
def _patch_egg_dir(path):
327 337
    # let's check if it's already patched
......
343 353

  
344 354
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
345 355

  
356

  
346 357
def _before_install():
347 358
    log.warn('Before install bootstrap.')
348 359
    _fake_setuptools()
......
351 362
def _under_prefix(location):
352 363
    if 'install' not in sys.argv:
353 364
        return True
354
    args = sys.argv[sys.argv.index('install')+1:]
365
    args = sys.argv[sys.argv.index('install') + 1:]
355 366
    for index, arg in enumerate(args):
356 367
        for option in ('--root', '--prefix'):
357 368
            if arg.startswith('%s=' % option):
......
359 370
                return location.startswith(top_dir)
360 371
            elif arg == option:
361 372
                if len(args) > index:
362
                    top_dir = args[index+1]
373
                    top_dir = args[index + 1]
363 374
                    return location.startswith(top_dir)
364 375
        if arg == '--user' and USER_SITE is not None:
365 376
            return location.startswith(USER_SITE)
......
380 391
                                  replacement=False))
381 392
    except TypeError:
382 393
        # old distribute API
383
        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
394
        setuptools_dist = ws.find(
395
            pkg_resources.Requirement.parse('setuptools'))
384 396

  
385 397
    if setuptools_dist is None:
386 398
        log.warn('No setuptools distribution found')
......
406 418
        log.warn('Egg installation')
407 419
        pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
408 420
        if (os.path.exists(pkg_info) and
409
            _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
421
                _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
410 422
            log.warn('Already patched.')
411 423
            return
412 424
        log.warn('Patching...')
......
448 460
            # Extract directories with a safe mode.
449 461
            directories.append(tarinfo)
450 462
            tarinfo = copy.copy(tarinfo)
451
            tarinfo.mode = 448 # decimal for oct 0700
463
            tarinfo.mode = 448  # decimal for oct 0700
452 464
        self.extract(tarinfo, path)
453 465

  
454 466
    # Reverse sort directories.
b/snf-pithos-app/pithos/api/delegate.py
1 1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
......
46 46

  
47 47
logger = logging.getLogger(__name__)
48 48

  
49

  
49 50
def delegate_to_login_service(request):
50 51
    url = AUTHENTICATION_URL
51 52
    users = AUTHENTICATION_USERS
52 53
    if users or not url:
53 54
        return HttpResponseNotFound()
54
    
55

  
55 56
    p = urlparse(url)
56 57
    if request.is_secure():
57 58
        proto = 'https://'
......
61 62
    uri = proto + p.netloc + '/login?' + urlencode(params)
62 63
    return HttpResponseRedirect(uri)
63 64

  
65

  
64 66
@csrf_exempt
65 67
def delegate_to_feedback_service(request):
66 68
    url = AUTHENTICATION_URL
67 69
    users = AUTHENTICATION_USERS
68 70
    if users or not url:
69 71
        return HttpResponseNotFound()
70
    
72

  
71 73
    p = urlparse(url)
72 74
    if request.is_secure():
73 75
        proto = 'https://'
74 76
    else:
75 77
        proto = 'http://'
76
    
78

  
77 79
    uri = proto + p.netloc + '/im/service/api/v2.0/feedback'
78
    headers = { 'X-Auth-Token' : SERVICE_TOKEN }
80
    headers = {'X-Auth-Token': SERVICE_TOKEN}
79 81
    values = dict([(k, v) for k, v in request.POST.items()])
80 82
    data = urllib.urlencode(values)
81 83
    req = urllib2.Request(uri, data, headers)
......
87 89
    except urllib2.URLError, e:
88 90
        logger.exception(e)
89 91
        return HttpResponse(status=e.reason)
90
    return HttpResponse()
92
    return HttpResponse()
b/snf-pithos-app/pithos/api/dispatch.py
1 1
from pithos.api.settings import (BACKEND_DB_MODULE, BACKEND_DB_CONNECTION,
2
                                    BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH,
3
                                    BACKEND_BLOCK_UMASK,
4
                                    BACKEND_QUEUE_MODULE, BACKEND_QUEUE_CONNECTION,
5
                                    BACKEND_QUOTA, BACKEND_VERSIONING)
2
                                 BACKEND_BLOCK_MODULE, BACKEND_BLOCK_PATH,
3
                                 BACKEND_BLOCK_UMASK,
4
                                 BACKEND_QUEUE_MODULE, BACKEND_QUEUE_CONNECTION,
5
                                 BACKEND_QUOTA, BACKEND_VERSIONING)
6 6
from pithos.backends import connect_backend
7 7
from pithos.api.util import hashmap_md5
8 8

  
......
14 14
import socket
15 15
from smtplib import SMTPException
16 16

  
17

  
17 18
def update_md5(m):
18 19
    if m['resource'] != 'object' or m['details']['action'] != 'object update':
19 20
        return
20
    
21

  
21 22
    backend = connect_backend(db_module=BACKEND_DB_MODULE,
22 23
                              db_connection=BACKEND_DB_CONNECTION,
23 24
                              block_module=BACKEND_BLOCK_MODULE,
......
27 28
                              queue_connection=BACKEND_QUEUE_CONNECTION)
28 29
    backend.default_policy['quota'] = BACKEND_QUOTA
29 30
    backend.default_policy['versioning'] = BACKEND_VERSIONING
30
    
31

  
31 32
    path = m['value']
32 33
    account, container, name = path.split('/', 2)
33 34
    version = m['details']['version']
34 35
    meta = None
35 36
    try:
36
        meta = backend.get_object_meta(account, account, container, name, 'pithos', version)
37
        meta = backend.get_object_meta(
38
            account, account, container, name, 'pithos', version)
37 39
        if meta['checksum'] == '':
38
            size, hashmap = backend.get_object_hashmap(account, account, container, name, version)
40
            size, hashmap = backend.get_object_hashmap(
41
                account, account, container, name, version)
39 42
            checksum = hashmap_md5(backend, hashmap, size)
40
            backend.update_object_checksum(account, account, container, name, version, checksum)
43
            backend.update_object_checksum(
44
                account, account, container, name, version, checksum)
41 45
            print 'INFO: Updated checksum for path "%s"' % (path,)
42 46
    except Exception, e:
43 47
        print 'WARNING: Can not update checksum for path "%s" (%s)' % (path, e)
44
    
48

  
45 49
    backend.close()
46 50

  
51

  
47 52
def send_sharing_notification(m):
48 53
    if m['resource'] != 'sharing':
49 54
        return
50
    
55

  
51 56
    members = m['details']['members']
52 57
    user = m['details']['user']
53 58
    path = m['value']
54 59
    account, container, name = path.split('/', 2)
55
    
60

  
56 61
    subject = 'Invitation to a Pithos+ shared object'
57 62
    from_email = DEFAULT_FROM_EMAIL
58 63
    recipient_list = members
59
    message = 'User %s has invited you to a Pithos+ shared object. You can view it under "Shared to me" at "%s".' %(user, path)
64
    message = 'User %s has invited you to a Pithos+ shared object. You can view it under "Shared to me" at "%s".' % (user, path)
60 65
    try:
61 66
        send_mail(subject, message, from_email, recipient_list)
62
        print 'INFO: Sharing notification sent for path "%s" to %s' % (path, ','.join(recipient_list))
67
        print 'INFO: Sharing notification sent for path "%s" to %s' % (
68
            path, ','.join(recipient_list))
63 69
    except (SMTPException, socket.error) as e:
64
        print 'WARNING: Can not update send email for sharing "%s" (%s)' % (path, e)
70
        print 'WARNING: Can not update send email for sharing "%s" (%s)' % (
71
            path, e)
b/snf-pithos-app/pithos/api/faults.py
1 1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34

  
34 35
def camelCase(s):
35 36
    return s[0].lower() + s[1:]
36 37

  
......
42 43
        self.details = details
43 44
        self.name = name or camelCase(self.__class__.__name__)
44 45

  
46

  
45 47
class NotModified(Fault):
46 48
    code = 304
47 49

  
50

  
48 51
class BadRequest(Fault):
49 52
    code = 400
50 53

  
54

  
51 55
class Unauthorized(Fault):
52 56
    code = 401
53 57

  
58

  
54 59
class Forbidden(Fault):
55 60
    code = 403
56 61

  
62

  
57 63
class ItemNotFound(Fault):
58 64
    code = 404
59 65

  
66

  
60 67
class Conflict(Fault):
61 68
    code = 409
62 69

  
70

  
63 71
class LengthRequired(Fault):
64 72
    code = 411
65 73

  
74

  
66 75
class PreconditionFailed(Fault):
67 76
    code = 412
68 77

  
78

  
69 79
class RequestEntityTooLarge(Fault):
70 80
    code = 413
71 81

  
82

  
72 83
class RangeNotSatisfiable(Fault):
73 84
    code = 416
74 85

  
86

  
75 87
class UnprocessableEntity(Fault):
76 88
    code = 422
77 89

  
90

  
78 91
class InternalServerError(Fault):
79 92
    code = 500
80 93

  
94

  
81 95
class NotImplemented(Fault):
82 96
    code = 501
b/snf-pithos-app/pithos/api/functions.py
1 1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
......
44 44

  
45 45
from synnefo.lib.astakos import get_user
46 46

  
47
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound, Conflict,
47
from pithos.api.faults import (
48
    Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound, Conflict,
48 49
    LengthRequired, PreconditionFailed, RequestEntityTooLarge, RangeNotSatisfiable, UnprocessableEntity)
49
from pithos.api.util import (json_encode_decimal, rename_meta_key, format_header_key, printable_header_dict,
50
from pithos.api.util import (
51
    json_encode_decimal, rename_meta_key, format_header_key, printable_header_dict,
50 52
    get_account_headers, put_account_headers, get_container_headers, put_container_headers, get_object_headers,
51 53
    put_object_headers, update_manifest_meta, update_sharing_meta, update_public_meta,
52 54
    validate_modification_preconditions, validate_matching_preconditions, split_container_object_string,
......
68 70
@csrf_exempt
69 71
def top_demux(request):
70 72
    if request.method == 'GET':
71
    	try:
72
    		request.GET['X-Auth-Token']
73
    	except KeyError:
74
    		try:
75
    			request.META['HTTP_X_AUTH_TOKEN']
76
    		except KeyError:
77
    			return authenticate(request)
78
    	return account_list(request)
73
        try:
74
            request.GET['X-Auth-Token']
75
        except KeyError:
76
            try:
77
                request.META['HTTP_X_AUTH_TOKEN']
78
            except KeyError:
79
                return authenticate(request)
80
        return account_list(request)
79 81
    else:
80 82
        return method_not_allowed(request)
81 83

  
84

  
82 85
@csrf_exempt
83 86
def account_demux(request, v_account):
84 87
    if request.method == 'HEAD':
......
90 93
    else:
91 94
        return method_not_allowed(request)
92 95

  
96

  
93 97
@csrf_exempt
94 98
def container_demux(request, v_account, v_container):
95 99
    if request.method == 'HEAD':
......
105 109
    else:
106 110
        return method_not_allowed(request)
107 111

  
112

  
108 113
@csrf_exempt
109 114
def object_demux(request, v_account, v_container, v_object):
110 115
    # Helper to avoid placing the token in the URL when loading objects from a browser.
......
127 132
    else:
128 133
        return method_not_allowed(request)
129 134

  
135

  
130 136
@api_method('GET', user_required=False)
131 137
def authenticate(request):
132 138
    # Normal Response Codes: 204
133 139
    # Error Response Codes: internalServerError (500),
134 140
    #                       forbidden (403),
135 141
    #                       badRequest (400)
136
    
142

  
137 143
    x_auth_user = request.META.get('HTTP_X_AUTH_USER')
138 144
    x_auth_key = request.META.get('HTTP_X_AUTH_KEY')
139 145
    if not x_auth_user or not x_auth_key:
140 146
        raise BadRequest('Missing X-Auth-User or X-Auth-Key header')
141 147
    response = HttpResponse(status=204)
142
    
148

  
143 149
    uri = request.build_absolute_uri()
144 150
    if '?' in uri:
145 151
        uri = uri[:uri.find('?')]
146
    
152

  
147 153
    response['X-Auth-Token'] = x_auth_key
148
    response['X-Storage-Url'] = uri + ('' if uri.endswith('/') else '/') + x_auth_user
154
    response['X-Storage-Url'] = uri + ('' if uri.endswith('/')
155
                                       else '/') + x_auth_user
149 156
    return response
150 157

  
158

  
151 159
@api_method('GET', format_allowed=True)
152 160
def account_list(request):
153 161
    # Normal Response Codes: 200, 204
154 162
    # Error Response Codes: internalServerError (500),
155 163
    #                       badRequest (400)
156 164
    response = HttpResponse()
157
    
165

  
158 166
    marker = request.GET.get('marker')
159 167
    limit = get_int_parameter(request.GET.get('limit'))
160 168
    if not limit:
161 169
        limit = 10000
162
    
170

  
163 171
    accounts = request.backend.list_accounts(request.user_uniq, marker, limit)
164
    
172

  
165 173
    if request.serialization == 'text':
166 174
        if len(accounts) == 0:
167 175
            # The cloudfiles python bindings expect 200 if json/xml.
......
170 178
        response.status_code = 200
171 179
        response.content = '\n'.join(accounts) + '\n'
172 180
        return response
173
    
181

  
174 182
    account_meta = []
175 183
    for x in accounts:
176 184
        if x == request.user_uniq:
177 185
            continue
178 186
        try:
179
            meta = request.backend.get_account_meta(request.user_uniq, x, 'pithos', include_user_defined=False)
187
            meta = request.backend.get_account_meta(
188
                request.user_uniq, x, 'pithos', include_user_defined=False)
180 189
            groups = request.backend.get_account_groups(request.user_uniq, x)
181 190
        except NotAllowedError:
182 191
            raise Forbidden('Not allowed')
183 192
        else:
184 193
            rename_meta_key(meta, 'modified', 'last_modified')
185
            rename_meta_key(meta, 'until_timestamp', 'x_account_until_timestamp')
194
            rename_meta_key(
195
                meta, 'until_timestamp', 'x_account_until_timestamp')
186 196
            if groups:
187
                meta['X-Account-Group'] = printable_header_dict(dict([(k, ','.join(v)) for k, v in groups.iteritems()]))
197
                meta['X-Account-Group'] = printable_header_dict(
198
                    dict([(k, ','.join(v)) for k, v in groups.iteritems()]))
188 199
            account_meta.append(printable_header_dict(meta))
189 200
    if request.serialization == 'xml':
190 201
        data = render_to_string('accounts.xml', {'accounts': account_meta})
191
    elif request.serialization  == 'json':
202
    elif request.serialization == 'json':
192 203
        data = json.dumps(account_meta)
193 204
    response.status_code = 200
194 205
    response.content = data
195 206
    return response
196 207

  
208

  
197 209
@api_method('HEAD')
198 210
def account_meta(request, v_account):
199 211
    # Normal Response Codes: 204
200 212
    # Error Response Codes: internalServerError (500),
201 213
    #                       forbidden (403),
202 214
    #                       badRequest (400)
203
    
215

  
204 216
    until = get_int_parameter(request.GET.get('until'))
205 217
    try:
206
        meta = request.backend.get_account_meta(request.user_uniq, v_account, 'pithos', until)
207
        groups = request.backend.get_account_groups(request.user_uniq, v_account)
208
        policy = request.backend.get_account_policy(request.user_uniq, v_account)
218
        meta = request.backend.get_account_meta(
219
            request.user_uniq, v_account, 'pithos', until)
220
        groups = request.backend.get_account_groups(
221
            request.user_uniq, v_account)
222
        policy = request.backend.get_account_policy(
223
            request.user_uniq, v_account)
209 224
    except NotAllowedError:
210 225
        raise Forbidden('Not allowed')
211
    
226

  
212 227
    validate_modification_preconditions(request, meta)
213
    
228

  
214 229
    response = HttpResponse(status=204)
215 230
    put_account_headers(response, meta, groups, policy)
216 231
    return response
217 232

  
233

  
218 234
@api_method('POST')
219 235
def account_update(request, v_account):
220 236
    # Normal Response Codes: 202
221 237
    # Error Response Codes: internalServerError (500),
222 238
    #                       forbidden (403),
223 239
    #                       badRequest (400)
224
    
240

  
225 241
    meta, groups = get_account_headers(request)
226 242
    replace = True
227 243
    if 'update' in request.GET:
......
229 245
    if groups:
230 246
        try:
231 247
            request.backend.update_account_groups(request.user_uniq, v_account,
232
                                                    groups, replace)
248
                                                  groups, replace)
233 249
        except NotAllowedError:
234 250
            raise Forbidden('Not allowed')
235 251
        except ValueError:
......
242 258
            raise Forbidden('Not allowed')
243 259
    return HttpResponse(status=202)
244 260

  
261

  
245 262
@api_method('GET', format_allowed=True)
246 263
def container_list(request, v_account):
247 264
    # Normal Response Codes: 200, 204
......
249 266
    #                       itemNotFound (404),
250 267
    #                       forbidden (403),
251 268
    #                       badRequest (400)
252
    
269

  
253 270
    until = get_int_parameter(request.GET.get('until'))
254 271
    try:
255
        meta = request.backend.get_account_meta(request.user_uniq, v_account, 'pithos', until)
256
        groups = request.backend.get_account_groups(request.user_uniq, v_account)
257
        policy = request.backend.get_account_policy(request.user_uniq, v_account)
272
        meta = request.backend.get_account_meta(
273
            request.user_uniq, v_account, 'pithos', until)
274
        groups = request.backend.get_account_groups(
275
            request.user_uniq, v_account)
276
        policy = request.backend.get_account_policy(
277
            request.user_uniq, v_account)
258 278
    except NotAllowedError:
259 279
        raise Forbidden('Not allowed')
260
    
280

  
261 281
    validate_modification_preconditions(request, meta)
262
    
282

  
263 283
    response = HttpResponse()
264 284
    put_account_headers(response, meta, groups, policy)
265
    
285

  
266 286
    marker = request.GET.get('marker')
267 287
    limit = get_int_parameter(request.GET.get('limit'))
268 288
    if not limit:
269 289
        limit = 10000
270
    
290

  
271 291
    shared = False
272 292
    if 'shared' in request.GET:
273 293
        shared = True
274 294
    public = False
275 295
    if 'public' in request.GET:
276 296
        public = True
277
    
297

  
278 298
    try:
279
        containers = request.backend.list_containers(request.user_uniq, v_account,
280
                                                marker, limit, shared, until, public)
299
        containers = request.backend.list_containers(
300
            request.user_uniq, v_account,
301
            marker, limit, shared, until, public)
281 302
    except NotAllowedError:
282 303
        raise Forbidden('Not allowed')
283 304
    except NameError:
284 305
        containers = []
285
    
306

  
286 307
    if request.serialization == 'text':
287 308
        if len(containers) == 0:
288 309
            # The cloudfiles python bindings expect 200 if json/xml.
......
291 312
        response.status_code = 200
292 313
        response.content = '\n'.join(containers) + '\n'
293 314
        return response
294
    
315

  
295 316
    container_meta = []
296 317
    for x in containers:
297 318
        try:
298
            meta = request.backend.get_container_meta(request.user_uniq, v_account,
299
                                                        x, 'pithos', until, include_user_defined=False)
319
            meta = request.backend.get_container_meta(
320
                request.user_uniq, v_account,
321
                x, 'pithos', until, include_user_defined=False)
300 322
            policy = request.backend.get_container_policy(request.user_uniq,
301
                                                            v_account, x)
323
                                                          v_account, x)
302 324
        except NotAllowedError:
303 325
            raise Forbidden('Not allowed')
304 326
        except NameError:
305 327
            pass
306 328
        else:
307 329
            rename_meta_key(meta, 'modified', 'last_modified')
308
            rename_meta_key(meta, 'until_timestamp', 'x_container_until_timestamp')
330
            rename_meta_key(
331
                meta, 'until_timestamp', 'x_container_until_timestamp')
309 332
            if policy:
310
                meta['X-Container-Policy'] = printable_header_dict(dict([(k, v) for k, v in policy.iteritems()]))
333
                meta['X-Container-Policy'] = printable_header_dict(
334
                    dict([(k, v) for k, v in policy.iteritems()]))
311 335
            container_meta.append(printable_header_dict(meta))
312 336
    if request.serialization == 'xml':
313
        data = render_to_string('containers.xml', {'account': v_account, 'containers': container_meta})
314
    elif request.serialization  == 'json':
337
        data = render_to_string('containers.xml', {'account':
338
                                v_account, 'containers': container_meta})
339
    elif request.serialization == 'json':
315 340
        data = json.dumps(container_meta)
316 341
    response.status_code = 200
317 342
    response.content = data
318 343
    return response
319 344

  
345

  
320 346
@api_method('HEAD')
321 347
def container_meta(request, v_account, v_container):
322 348
    # Normal Response Codes: 204
......
324 350
    #                       itemNotFound (404),
325 351
    #                       forbidden (403),
326 352
    #                       badRequest (400)
327
    
353

  
328 354
    until = get_int_parameter(request.GET.get('until'))
329 355
    try:
330 356
        meta = request.backend.get_container_meta(request.user_uniq, v_account,
331
                                                    v_container, 'pithos', until)
357
                                                  v_container, 'pithos', until)
332 358
        meta['object_meta'] = request.backend.list_container_meta(request.user_uniq,
333
                                                v_account, v_container, 'pithos', until)
334
        policy = request.backend.get_container_policy(request.user_uniq, v_account,
335
                                                        v_container)
359
                                                                  v_account, v_container, 'pithos', until)
360
        policy = request.backend.get_container_policy(
361
            request.user_uniq, v_account,
362
            v_container)
336 363
    except NotAllowedError:
337 364
        raise Forbidden('Not allowed')
338 365
    except ItemNotExists:
339 366
        raise ItemNotFound('Container does not exist')
340
    
367

  
341 368
    validate_modification_preconditions(request, meta)
342
    
369

  
343 370
    response = HttpResponse(status=204)
344 371
    put_container_headers(request, response, meta, policy)
345 372
    return response
346 373

  
374

  
347 375
@api_method('PUT')
348 376
def container_create(request, v_account, v_container):
349 377
    # Normal Response Codes: 201, 202
......
351 379
    #                       itemNotFound (404),
352 380
    #                       forbidden (403),
353 381
    #                       badRequest (400)
354
    
382

  
355 383
    meta, policy = get_container_headers(request)
356
    
384

  
357 385
    try:
358
        request.backend.put_container(request.user_uniq, v_account, v_container, policy)
386
        request.backend.put_container(
387
            request.user_uniq, v_account, v_container, policy)
359 388
        ret = 201
360 389
    except NotAllowedError:
361 390
        raise Forbidden('Not allowed')
......
363 392
        raise BadRequest('Invalid policy header')
364 393
    except NameError:
365 394
        ret = 202
366
    
395

  
367 396
    if ret == 202 and policy:
368 397
        try:
369
            request.backend.update_container_policy(request.user_uniq, v_account,
370
                                            v_container, policy, replace=False)
398
            request.backend.update_container_policy(
399
                request.user_uniq, v_account,
400
                v_container, policy, replace=False)
371 401
        except NotAllowedError:
372 402
            raise Forbidden('Not allowed')
373 403
        except ItemNotExists:
......
377 407
    if meta:
378 408
        try:
379 409
            request.backend.update_container_meta(request.user_uniq, v_account,
380
                                            v_container, 'pithos', meta, replace=False)
410
                                                  v_container, 'pithos', meta, replace=False)
381 411
        except NotAllowedError:
382 412
            raise Forbidden('Not allowed')
383 413
        except ItemNotExists:
384 414
            raise ItemNotFound('Container does not exist')
385
    
415

  
386 416
    return HttpResponse(status=ret)
387 417

  
418

  
388 419
@api_method('POST', format_allowed=True)
389 420
def container_update(request, v_account, v_container):
390 421
    # Normal Response Codes: 202
......
392 423
    #                       itemNotFound (404),
393 424
    #                       forbidden (403),
394 425
    #                       badRequest (400)
395
    
426

  
396 427
    meta, policy = get_container_headers(request)
397 428
    replace = True
398 429
    if 'update' in request.GET:
399 430
        replace = False
400 431
    if policy:
401 432
        try:
402
            request.backend.update_container_policy(request.user_uniq, v_account,
403
                                                v_container, policy, replace)
433
            request.backend.update_container_policy(
434
                request.user_uniq, v_account,
435
                v_container, policy, replace)
404 436
        except NotAllowedError:
405 437
            raise Forbidden('Not allowed')
406 438
        except ItemNotExists:
......
410 442
    if meta or replace:
411 443
        try:
412 444
            request.backend.update_container_meta(request.user_uniq, v_account,
413
                                                    v_container, 'pithos', meta, replace)
445
                                                  v_container, 'pithos', meta, replace)
414 446
        except NotAllowedError:
415 447
            raise Forbidden('Not allowed')
416 448
        except ItemNotExists:
417 449
            raise ItemNotFound('Container does not exist')
418
    
450

  
419 451
    content_length = -1
420 452
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
421
        content_length = get_int_parameter(request.META.get('CONTENT_LENGTH', 0))
453
        content_length = get_int_parameter(
454
            request.META.get('CONTENT_LENGTH', 0))
422 455
    content_type = request.META.get('CONTENT_TYPE')
423 456
    hashmap = []
424 457
    if content_type and content_type == 'application/octet-stream' and content_length != 0:
425 458
        for data in socket_read_iterator(request, content_length,
426
                                            request.backend.block_size):
459
                                         request.backend.block_size):
427 460
            # TODO: Raise 408 (Request Timeout) if this takes too long.
428 461
            # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data.
429 462
            hashmap.append(request.backend.put_block(data))
430
    
463

  
431 464
    response = HttpResponse(status=202)
432 465
    if hashmap:
433 466
        response.content = simple_list_response(request, hashmap)
434 467
    return response
435 468

  
469

  
436 470
@api_method('DELETE')
437 471
def container_delete(request, v_account, v_container):
438 472
    # Normal Response Codes: 204
......
441 475
    #                       itemNotFound (404),
442 476
    #                       forbidden (403),
443 477
    #                       badRequest (400)
444
    
478

  
445 479
    until = get_int_parameter(request.GET.get('until'))
446
    
480

  
447 481
    delimiter = request.GET.get('delimiter')
448
    
482

  
449 483
    try:
450
        request.backend.delete_container(request.user_uniq, v_account, v_container,
451
                                            until, delimiter=delimiter)
484
        request.backend.delete_container(
485
            request.user_uniq, v_account, v_container,
486
            until, delimiter=delimiter)
452 487
    except NotAllowedError:
453 488
        raise Forbidden('Not allowed')
454 489
    except ItemNotExists:
......
457 492
        raise Conflict('Container is not empty')
458 493
    return HttpResponse(status=204)
459 494

  
495

  
460 496
@api_method('GET', format_allowed=True)
461 497
def object_list(request, v_account, v_container):
462 498
    # Normal Response Codes: 200, 204
......
464 500
    #                       itemNotFound (404),
465 501
    #                       forbidden (403),
466 502
    #                       badRequest (400)
467
    
503

  
468 504
    until = get_int_parameter(request.GET.get('until'))
469 505
    try:
470 506
        meta = request.backend.get_container_meta(request.user_uniq, v_account,
471
                                                    v_container, 'pithos', until)
507
                                                  v_container, 'pithos', until)
472 508
        meta['object_meta'] = request.backend.list_container_meta(request.user_uniq,
473
                                                v_account, v_container, 'pithos', until)
474
        policy = request.backend.get_container_policy(request.user_uniq, v_account,
475
                                                        v_container)
509
                                                                  v_account, v_container, 'pithos', until)
510
        policy = request.backend.get_container_policy(
511
            request.user_uniq, v_account,
512
            v_container)
476 513
    except NotAllowedError:
477 514
        raise Forbidden('Not allowed')
478 515
    except ItemNotExists:
479 516
        raise ItemNotFound('Container does not exist')
480
    
517

  
481 518
    validate_modification_preconditions(request, meta)
482
    
519

  
483 520
    response = HttpResponse()
484 521
    put_container_headers(request, response, meta, policy)
485
    
522

  
486 523
    path = request.GET.get('path')
487 524
    prefix = request.GET.get('prefix')
488 525
    delimiter = request.GET.get('delimiter')
489
    
526

  
490 527
    # Path overrides prefix and delimiter.
491 528
    virtual = True
492 529
    if path:
493 530
        prefix = path
494 531
        delimiter = '/'
495 532
        virtual = False
496
    
533

  
497 534
    # Naming policy.
498 535
    if prefix and delimiter and not prefix.endswith(delimiter):
499 536
        prefix = prefix + delimiter
500 537
    if not prefix:
501 538
        prefix = ''
502 539
    prefix = prefix.lstrip('/')
503
    
540

  
504 541
    marker = request.GET.get('marker')
505 542
    limit = get_int_parameter(request.GET.get('limit'))
506 543
    if not limit:
507 544
        limit = 10000
508
    
545

  
509 546
    keys = request.GET.get('meta')
510 547
    if keys:
511
        keys = [smart_str(x.strip()) for x in keys.split(',') if x.strip() != '']
548
        keys = [smart_str(x.strip()) for x in keys.split(',')
549
                if x.strip() != '']
512 550
        included, excluded, opers = parse_filters(keys)
513 551
        keys = []
514 552
        keys += [format_header_key('X-Object-Meta-' + x) for x in included]
515
        keys += ['!'+format_header_key('X-Object-Meta-' + x) for x in excluded]
516
        keys += ['%s%s%s' % (format_header_key('X-Object-Meta-' + k), o, v) for k, o, v in opers]
553
        keys += ['!' + format_header_key('X-Object-Meta-' + x)
554
                 for x in excluded]
555
        keys += ['%s%s%s' % (format_header_key(
556
            'X-Object-Meta-' + k), o, v) for k, o, v in opers]
517 557
    else:
518 558
        keys = []
519
    
559

  
520 560
    shared = False
521 561
    if 'shared' in request.GET:
522 562
        shared = True
523 563
    public = False
524 564
    if 'public' in request.GET:
525 565
        public = True
526
    
566

  
527 567
    if request.serialization == 'text':
528 568
        try:
529
            objects = request.backend.list_objects(request.user_uniq, v_account,
530
                                        v_container, prefix, delimiter, marker,
531
                                        limit, virtual, 'pithos', keys, shared,
532
                                        until, None, public)
569
            objects = request.backend.list_objects(
570
                request.user_uniq, v_account,
571
                v_container, prefix, delimiter, marker,
572
                limit, virtual, 'pithos', keys, shared,
573
                until, None, public)
533 574
        except NotAllowedError:
534 575
            raise Forbidden('Not allowed')
535 576
        except ItemNotExists:
536 577
            raise ItemNotFound('Container does not exist')
537
        
578

  
538 579
        if len(objects) == 0:
539 580
            # The cloudfiles python bindings expect 200 if json/xml.
540 581
            response.status_code = 204
......
544 585
        return response
545 586

  
546 587
    try:
547
        objects = request.backend.list_object_meta(request.user_uniq, v_account,
548
                                    v_container, prefix, delimiter, marker,
549
                                    limit, virtual, 'pithos', keys, shared, until, None, public)
588
        objects = request.backend.list_object_meta(
589
            request.user_uniq, v_account,
590
            v_container, prefix, delimiter, marker,
591
            limit, virtual, 'pithos', keys, shared, until, None, public)
550 592
        object_permissions = {}
551 593
        object_public = {}
552 594
        if until is None:
553 595
            name_idx = len('/'.join((v_account, v_container, '')))
554 596
            for x in request.backend.list_object_permissions(request.user_uniq,
555
                                    v_account, v_container, prefix):
597
                                                             v_account, v_container, prefix):
556 598
                object = x[name_idx:]
557 599
                object_permissions[object] = request.backend.get_object_permissions(
558
                                    request.user_uniq, v_account, v_container, object)
600
                    request.user_uniq, v_account, v_container, object)
559 601
            for k, v in request.backend.list_object_public(request.user_uniq,
560
                                    v_account, v_container, prefix).iteritems():
602
                                                           v_account, v_container, prefix).iteritems():
561 603
                object_public[k[name_idx:]] = v
562 604
    except NotAllowedError:
563 605
        raise Forbidden('Not allowed')
564 606
    except ItemNotExists:
565 607
        raise ItemNotFound('Container does not exist')
566
    
608

  
567 609
    object_meta = []
568 610
    for meta in objects:
569 611
        if len(meta) == 1:
570 612
            # Virtual objects/directories.
571 613
            object_meta.append(meta)
572 614
        else:
573
            rename_meta_key(meta, 'hash', 'x_object_hash') # Will be replaced by checksum.
615
            rename_meta_key(
616
                meta, 'hash', 'x_object_hash')  # Will be replaced by checksum.
574 617
            rename_meta_key(meta, 'checksum', 'hash')
575 618
            rename_meta_key(meta, 'type', 'content_type')
576 619
            rename_meta_key(meta, 'uuid', 'x_object_uuid')
......
580 623
                rename_meta_key(meta, 'modified', 'last_modified')
581 624
            rename_meta_key(meta, 'modified_by', 'x_object_modified_by')
582 625
            rename_meta_key(meta, 'version', 'x_object_version')
583
            rename_meta_key(meta, 'version_timestamp', 'x_object_version_timestamp')
626
            rename_meta_key(
627
                meta, 'version_timestamp', 'x_object_version_timestamp')
584 628
            permissions = object_permissions.get(meta['name'], None)
585 629
            if permissions:
586
                update_sharing_meta(request, permissions, v_account, v_container, meta['name'], meta)
630
                update_sharing_meta(request, permissions, v_account,
631
                                    v_container, meta['name'], meta)
587 632
            public = object_public.get(meta['name'], None)
588 633
            if public:
589 634
                update_public_meta(public, meta)
590 635
            object_meta.append(printable_header_dict(meta))
591 636
    if request.serialization == 'xml':
592
        data = render_to_string('objects.xml', {'container': v_container, 'objects': object_meta})
593
    elif request.serialization  == 'json':
637
        data = render_to_string(
638
            'objects.xml', {'container': v_container, 'objects': object_meta})
639
    elif request.serialization == 'json':
594 640
        data = json.dumps(object_meta, default=json_encode_decimal)
595 641
    response.status_code = 200
596 642
    response.content = data
597 643
    return response
598 644

  
645

  
599 646
@api_method('HEAD')
600 647
def object_meta(request, v_account, v_container, v_object):
601 648
    # Normal Response Codes: 204
......
603 650
    #                       itemNotFound (404),
604 651
    #                       forbidden (403),
605 652
    #                       badRequest (400)
606
    
653

  
607 654
    version = request.GET.get('version')
608 655
    try:
609 656
        meta = request.backend.get_object_meta(request.user_uniq, v_account,
610
                                                v_container, v_object, 'pithos', version)
657
                                               v_container, v_object, 'pithos', version)
611 658
        if version is None:
612
            permissions = request.backend.get_object_permissions(request.user_uniq,
613
                                            v_account, v_container, v_object)
614
            public = request.backend.get_object_public(request.user_uniq, v_account,
615
                                                        v_container, v_object)
659
            permissions = request.backend.get_object_permissions(
660
                request.user_uniq,
661
                v_account, v_container, v_object)
662
            public = request.backend.get_object_public(
663
                request.user_uniq, v_account,
664
                v_container, v_object)
616 665
        else:
617 666
            permissions = None
618 667
            public = None
......
622 671
        raise ItemNotFound('Object does not exist')
623 672
    except VersionNotExists:
624 673
        raise ItemNotFound('Version does not exist')
625
    
674

  
626 675
    update_manifest_meta(request, v_account, meta)
627
    update_sharing_meta(request, permissions, v_account, v_container, v_object, meta)
676
    update_sharing_meta(
677
        request, permissions, v_account, v_container, v_object, meta)
628 678
    update_public_meta(public, meta)
629
    
679

  
630 680
    # Evaluate conditions.
631 681
    validate_modification_preconditions(request, meta)
632 682
    try:
......
635 685
        response = HttpResponse(status=304)
636 686
        response['ETag'] = meta['checksum']
637 687
        return response
638
    
688

  
639 689
    response = HttpResponse(status=200)
640 690
    put_object_headers(response, meta)
641 691
    return response
642 692

  
693

  
643 694
@api_method('GET', format_allowed=True)
644 695
def object_read(request, v_account, v_container, v_object):
645 696
    # Normal Response Codes: 200, 206
......
650 701
    #                       forbidden (403),
651 702
    #                       badRequest (400),
652 703
    #                       notModified (304)
653
    
704

  
654 705
    version = request.GET.get('version')
655
    
706

  
656 707
    # Reply with the version list. Do this first, as the object may be deleted.
657 708
    if version == 'list':
658 709
        if request.serialization == 'text':
659 710
            raise BadRequest('No format specified for version list.')
660
        
711

  
661 712
        try:
662 713
            v = request.backend.list_versions(request.user_uniq, v_account,
663
                                                v_container, v_object)
714
                                              v_container, v_object)
664 715
        except NotAllowedError:
665 716
            raise Forbidden('Not allowed')
666 717
        d = {'versions': v}
667 718
        if request.serialization == 'xml':
668 719
            d['object'] = v_object
669 720
            data = render_to_string('versions.xml', d)
670
        elif request.serialization  == 'json':
721
        elif request.serialization == 'json':
671 722
            data = json.dumps(d, default=json_encode_decimal)
672
        
723

  
673 724
        response = HttpResponse(data, status=200)
674 725
        response['Content-Length'] = len(data)
675 726
        return response
676
    
727

  
677 728
    try:
678 729
        meta = request.backend.get_object_meta(request.user_uniq, v_account,
679
                                                v_container, v_object, 'pithos', version)
730
                                               v_container, v_object, 'pithos', version)
680 731
        if version is None:
681
            permissions = request.backend.get_object_permissions(request.user_uniq,
682
                                            v_account, v_container, v_object)
683
            public = request.backend.get_object_public(request.user_uniq, v_account,
684
                                                        v_container, v_object)
732
            permissions = request.backend.get_object_permissions(
733
                request.user_uniq,
734
                v_account, v_container, v_object)
735
            public = request.backend.get_object_public(
736
                request.user_uniq, v_account,
737
                v_container, v_object)
685 738
        else:
686 739
            permissions = None
687 740
            public = None
......
691 744
        raise ItemNotFound('Object does not exist')
692 745
    except VersionNotExists:
693 746
        raise ItemNotFound('Version does not exist')
694
    
747

  
695 748
    update_manifest_meta(request, v_account, meta)
696
    update_sharing_meta(request, permissions, v_account, v_container, v_object, meta)
749
    update_sharing_meta(
750
        request, permissions, v_account, v_container, v_object, meta)
697 751
    update_public_meta(public, meta)
698
    
752

  
699 753
    # Evaluate conditions.
700 754
    validate_modification_preconditions(request, meta)
701 755
    try:
......
704 758
        response = HttpResponse(status=304)
705 759
        response['ETag'] = meta['checksum']
706 760
        return response
707
    
761

  
708 762
    hashmap_reply = False
709 763
    if 'hashmap' in request.GET and request.serialization != 'text':
710 764
        hashmap_reply = True
711
    
765

  
712 766
    sizes = []
713 767
    hashmaps = []
714 768
    if 'X-Object-Manifest' in meta and not hashmap_reply:
715 769
        try:
716
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
717
            objects = request.backend.list_objects(request.user_uniq, v_account,
718
                                src_container, prefix=src_name, virtual=False)
770
            src_container, src_name = split_container_object_string(
771
                '/' + meta['X-Object-Manifest'])
772
            objects = request.backend.list_objects(
773
                request.user_uniq, v_account,
774
                src_container, prefix=src_name, virtual=False)
719 775
        except NotAllowedError:
720 776
            raise Forbidden('Not allowed')
721 777
        except ValueError:
722 778
            raise BadRequest('Invalid X-Object-Manifest header')
723 779
        except ItemNotExists:
724 780
            raise ItemNotFound('Container does not exist')
725
        
781

  
726 782
        try:
727 783
            for x in objects:
728 784
                s, h = request.backend.get_object_hashmap(request.user_uniq,
729
                                        v_account, src_container, x[0], x[1])
785
                                                          v_account, src_container, x[0], x[1])
730 786
                sizes.append(s)
731 787
                hashmaps.append(h)
732 788
        except NotAllowedError:
......
737 793
            raise ItemNotFound('Version does not exist')
738 794
    else:
739 795
        try:
740
            s, h = request.backend.get_object_hashmap(request.user_uniq, v_account,
741
                                                v_container, v_object, version)
796
            s, h = request.backend.get_object_hashmap(
797
                request.user_uniq, v_account,
798
                v_container, v_object, version)
742 799
            sizes.append(s)
743 800
            hashmaps.append(h)
744 801
        except NotAllowedError:
......
747 804
            raise ItemNotFound('Object does not exist')
748 805
        except VersionNotExists:
749 806
            raise ItemNotFound('Version does not exist')
750
    
807

  
751 808
    # Reply with the hashmap.
752 809
    if hashmap_reply:
753 810
        size = sum(sizes)
......
760 817
        if request.serialization == 'xml':
761 818
            d['object'] = v_object
762 819
            data = render_to_string('hashes.xml', d)
763
        elif request.serialization  == 'json':
820
        elif request.serialization == 'json':
764 821
            data = json.dumps(d)
765
        
822

  
766 823
        response = HttpResponse(data, status=200)
767 824
        put_object_headers(response, meta)
768 825
        response['Content-Length'] = len(data)
769 826
        return response
770
    
771
    request.serialization = 'text' # Unset.
827

  
828
    request.serialization = 'text'  # Unset.
772 829
    return object_data_response(request, sizes, hashmaps, meta)
773 830

  
831

  
774 832
@api_method('PUT', format_allowed=True)
775 833
def object_write(request, v_account, v_container, v_object):
776 834
    # Normal Response Codes: 201
......
781 839
    #                       itemNotFound (404),
782 840
    #                       forbidden (403),
783 841
    #                       badRequest (400)
784
    
842

  
785 843
    # Evaluate conditions.
786 844
    if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'):
787 845
        try:
788
            meta = request.backend.get_object_meta(request.user_uniq, v_account,
789
                                                        v_container, v_object, 'pithos')
846
            meta = request.backend.get_object_meta(
847
                request.user_uniq, v_account,
848
                v_container, v_object, 'pithos')
790 849
        except NotAllowedError:
791 850
            raise Forbidden('Not allowed')
792 851
        except NameError:
793 852
            meta = {}
794 853
        validate_matching_preconditions(request, meta)
795
    
854

  
796 855
    copy_from = request.META.get('HTTP_X_COPY_FROM')
797 856
    move_from = request.META.get('HTTP_X_MOVE_FROM')
798 857
    if copy_from or move_from:
799 858
        delimiter = request.GET.get('delimiter')
800
        content_length = get_content_length(request) # Required by the API.
801
        
859
        content_length = get_content_length(request)  # Required by the API.
860

  
802 861
        src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
803 862
        if not src_account:
804 863
            src_account = request.user_uniq
805 864
        if move_from:
806 865
            try:
807
                src_container, src_name = split_container_object_string(move_from)
866
                src_container, src_name = split_container_object_string(
867
                    move_from)
808 868
            except ValueError:
809 869
                raise BadRequest('Invalid X-Move-From header')
810
            version_id = copy_or_move_object(request, src_account, src_container, src_name,
811
                                                v_account, v_container, v_object, move=True, delimiter=delimiter)
870
            version_id = copy_or_move_object(
871
                request, src_account, src_container, src_name,
872
                v_account, v_container, v_object, move=True, delimiter=delimiter)
812 873
        else:
813 874
            try:
814
                src_container, src_name = split_container_object_string(copy_from)
875
                src_container, src_name = split_container_object_string(
876
                    copy_from)
815 877
            except ValueError:
816 878
                raise BadRequest('Invalid X-Copy-From header')
817
            version_id = copy_or_move_object(request, src_account, src_container, src_name,
818
                                                v_account, v_container, v_object, move=False, delimiter=delimiter)
879
            version_id = copy_or_move_object(
880
                request, src_account, src_container, src_name,
881
                v_account, v_container, v_object, move=False, delimiter=delimiter)
819 882
        response = HttpResponse(status=201)
820 883
        response['X-Object-Version'] = version_id
821 884
        return response
822
    
885

  
823 886
    content_type, meta, permissions, public = get_object_headers(request)
824 887
    content_length = -1
825 888
    if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
......
827 890
    # Should be BadRequest, but API says otherwise.
828 891
    if content_type is None:
829 892
        raise LengthRequired('Missing Content-Type header')
830
    
893

  
831 894
    if 'hashmap' in request.GET:
832 895
        if request.serialization not in ('json', 'xml'):
833 896
            raise BadRequest('Invalid hashmap format')
834
        
897

  
835 898
        data = ''
836 899
        for block in socket_read_iterator(request, content_length,
837
                                            request.backend.block_size):
900
                                          request.backend.block_size):
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff