from django.http import HttpResponse
from django.utils import simplejson as json
from django.utils.http import http_date, parse_etags
-from django.utils.encoding import smart_str
+from django.utils.encoding import smart_unicode, smart_str
from django.core.files.uploadhandler import FileUploadHandler
from django.core.files.uploadedfile import UploadedFile
-from pithos.api.compat import parse_http_date_safe, parse_http_date
+from pithos.lib.compat import parse_http_date_safe, parse_http_date
+from pithos.lib.hashmap import HashMap
+
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound,
Conflict, LengthRequired, PreconditionFailed, RequestEntityTooLarge,
RangeNotSatisfiable, ServiceUnavailable)
Format 'last_modified' timestamp.
"""
- d['last_modified'] = isoformat(datetime.fromtimestamp(d['last_modified']))
+ if 'last_modified' in d:
+ d['last_modified'] = isoformat(datetime.fromtimestamp(d['last_modified']))
return dict([(k.lower().replace('-', '_'), v) for k, v in d.iteritems()])
def format_header_key(k):
return meta, get_sharing(request), get_public(request)
def put_object_headers(response, meta, restricted=False):
- response['ETag'] = meta['ETag']
+ response['ETag'] = meta['ETag'] if 'ETag' in meta else meta['hash']
response['Content-Length'] = meta['bytes']
response['Content-Type'] = meta.get('Content-Type', 'application/octet-stream')
response['Last-Modified'] = http_date(int(meta['modified']))
else:
for k in ('Content-Encoding', 'Content-Disposition'):
if k in meta:
- response[k] = meta[k]
+ response[k] = smart_str(meta[k], strings_only=True)
def update_manifest_meta(request, v_account, meta):
"""Update metadata if the object has an X-Object-Manifest."""
if move:
version_id = request.backend.move_object(request.user_uniq, src_account, src_container, src_name,
dest_account, dest_container, dest_name,
- meta, False, permissions)
+ 'pithos', meta, False, permissions)
else:
version_id = request.backend.copy_object(request.user_uniq, src_account, src_container, src_name,
dest_account, dest_container, dest_name,
- meta, False, permissions, src_version)
+ 'pithos', meta, False, permissions, src_version)
except NotAllowedError:
raise Forbidden('Not allowed')
except (NameError, IndexError):
def hashmap_hash(request, hashmap):
"""Produce the root hash, treating the hashmap as a Merkle-like tree."""
- def subhash(d):
- h = hashlib.new(request.backend.hash_algorithm)
- h.update(d)
- return h.digest()
-
- if len(hashmap) == 0:
- return hexlify(subhash(''))
- if len(hashmap) == 1:
- return hashmap[0]
-
- s = 2
- while s < len(hashmap):
- s = s * 2
- h = [unhexlify(x) for x in hashmap]
- h += [('\x00' * len(h[0]))] * (s - len(hashmap))
- while len(h) > 1:
- h = [subhash(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
- return hexlify(h[0])
+ map = HashMap(request.backend.block_size, request.backend.hash_algorithm)
+ map.extend([unhexlify(x) for x in hashmap])
+ return hexlify(map.hash())
def update_request_headers(request):
# Handle URL-encoded keys and values.
- meta = request.META
- for k, v in meta.copy().iteritems():
- if ((k.startswith('HTTP_X_ACCOUNT_META_') or k.startswith('HTTP_X_ACCOUNT_GROUP_') or
- k.startswith('HTTP_X_CONTAINER_META_') or k.startswith('HTTP_X_OBJECT_META_') or
- k in ('HTTP_X_OBJECT_MANIFEST', 'HTTP_X_OBJECT_SHARING',
- 'HTTP_X_COPY_FROM', 'HTTP_X_MOVE_FROM',
- 'HTTP_X_SOURCE_ACCOUNT', 'HTTP_X_SOURCE_OBJECT',
- 'HTTP_DESTINATION_ACCOUNT', 'HTTP_DESTINATION')) and
- ('%' in k or '%' in v)):
- del(meta[k])
- meta[unquote(k)] = unquote(v)
+ # Handle URL-encoded keys and values.
+ meta = dict([(k, v) for k, v in request.META.iteritems() if k.startswith('HTTP_')])
+ if len(meta) > 90:
+ raise BadRequest('Too many headers.')
+ for k, v in meta.iteritems():
+ if len(k) > 128:
+ raise BadRequest('Header name too large.')
+ if len(v) > 256:
+ raise BadRequest('Header value too large.')
+ try:
+ k.decode('ascii')
+ v.decode('ascii')
+ except UnicodeDecodeError:
+ raise BadRequest('Bad character in headers.')
+ if '%' in k or '%' in v:
+ del(request.META[k])
+ request.META[unquote(k)] = smart_unicode(unquote(v), strings_only=True)
def update_response_headers(request, response):
if request.serialization == 'xml':
# URL-encode unicode in headers.
meta = response.items()
for k, v in meta:
- if (k.startswith('X-Account-Meta-') or k.startswith('X-Account-Group-') or
- k.startswith('X-Container-Meta-') or k.startswith('X-Object-Meta-') or
- k in ('X-Container-Object-Meta', 'X-Object-Manifest', 'X-Object-Sharing', 'X-Object-Shared-By')):
+ if (k.startswith('X-Account-') or k.startswith('X-Container-') or
+ k.startswith('X-Object-') or k.startswith('Content-')):
del(response[k])
- response[quote(k)] = quote(v, safe='/=,:@')
+ response[quote(k)] = quote(v, safe='/=,:@; ')
if settings.TEST:
response['Date'] = format_date_time(time())
# Format and check headers.
update_request_headers(request)
- meta = dict([(k, v) for k, v in request.META.iteritems() if k.startswith('HTTP_')])
- if len(meta) > 90:
- raise BadRequest('Too many headers.')
- for k, v in meta.iteritems():
- if len(k) > 128:
- raise BadRequest('Header name too large.')
- if len(v) > 256:
- raise BadRequest('Header value too large.')
# Fill in custom request variables.
request.serialization = request_serialization(request, format_allowed)