wsgi/__init__.py
changeset 0 b97547f5f1fa
child 1802 d628defebc17
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wsgi/__init__.py	Wed Nov 05 15:52:50 2008 +0100
@@ -0,0 +1,87 @@
+"""This package contains all WSGI specific code for cubicweb
+
+NOTE: this package borrows a lot of code to Django
+      (http://www.djangoproject.com) and to the wsgiref module
+      of the python2.5's stdlib.
+
+WSGI corresponding PEP: http://www.python.org/dev/peps/pep-0333/
+
+:organization: Logilab
+:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from email import message, message_from_string
+from Cookie import SimpleCookie
+from StringIO import StringIO
+from cgi import parse_header, parse_qsl
+from pprint import pformat as _pformat
+
+
+def pformat(obj):
+    """pretty prints `obj` if possible"""
+    try:
+        return _pformat(obj)
+    except:
+        return u'<could not parse>'
+    
+def qs2dict(qs):
+    """transforms a query string into a regular python dict"""
+    result = {}
+    for key, value in parse_qsl(qs, True):
+        result.setdefault(key, []).append(value)
+    return result
+
+def normalize_header(header):
+    """returns a normalized header name
+    
+    >>> normalize_header('User_Agent')
+    'User-agent'
+    """
+    return header.replace('_', '-').capitalize()
+
+def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
+    """
+    THIS COMES FROM DJANGO
+    A version of shutil.copyfileobj that will not read more than 'size' bytes.
+    This makes it safe from clients sending more than CONTENT_LENGTH bytes of
+    data in the body.
+    """
+    if not size:
+        return
+    while size > 0:
+        buf = fsrc.read(min(length, size))
+        if not buf:
+            break
+        fdst.write(buf)
+        size -= len(buf)
+
+def parse_file_upload(header_dict, post_data):
+    """This is adapted FROM DJANGO"""
+    raw_message = '\r\n'.join('%s:%s' % pair for pair in header_dict.iteritems())
+    raw_message += '\r\n\r\n' + post_data
+    msg = message_from_string(raw_message)
+    post, files = {}, {}
+    for submessage in msg.get_payload():
+        name_dict = parse_header(submessage['Content-Disposition'])[1]
+        key = name_dict['name']
+        # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads
+        # or {'name': 'blah'} for POST fields
+        # We assume all uploaded files have a 'filename' set.
+        if 'filename' in name_dict:
+            assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported"
+            if not name_dict['filename'].strip():
+                continue
+            # IE submits the full path, so trim everything but the basename.
+            # (We can't use os.path.basename because that uses the server's
+            # directory separator, which may not be the same as the
+            # client's one.)
+            filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:]
+            mimetype = 'Content-Type' in submessage and submessage['Content-Type'] or None
+            content = StringIO(submessage.get_payload())
+            files[key] = [filename, mimetype, content]
+        else:
+            post.setdefault(key, []).append(submessage.get_payload())
+    return post, files
+