charset changes in Publisher
David Binger <dbinger-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Tue, 15 Mar 2005 15:44:12 -0500
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
--Apple-Mail-5--531733632
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
Pay attention to charset parameters in requests.
Add support for changing the default charset.
--Apple-Mail-5--531733632
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
x-unix-mode=0664;
name="charset.diff.txt"
Content-Disposition: attachment;
filename=charset.diff.txt
Index: publish.py
===================================================================
--- publish.py (revision 26332)
+++ publish.py (revision 26338)
@@ -4,7 +4,7 @@
Logic for publishing modules and objects on the Web.
"""
-import sys, traceback, cStringIO
+import sys, traceback, StringIO
import time
import urlparse
import cgitb
@@ -211,7 +211,7 @@
def _generate_plaintext_error(self, request, original_response,
exc_type, exc_value, tb):
- error_file = cStringIO.StringIO()
+ error_file = StringIO.StringIO()
# format the traceback
traceback.print_exception(exc_type, exc_value, tb, file=error_file)
@@ -226,7 +226,7 @@
def _generate_cgitb_error(self, request, original_response,
exc_type, exc_value, tb):
- error_file = cStringIO.StringIO()
+ error_file = StringIO.StringIO()
hook = cgitb.Hook(file=error_file)
hook(exc_type, exc_value, tb)
error_file.write('<h2>Original Request</h2>')
Index: http_request.py
===================================================================
--- http_request.py (revision 26332)
+++ http_request.py (revision 26338)
@@ -47,6 +47,16 @@
else:
return None
+def _decode_string(s, charset):
+ if charset == 'iso-8859-1':
+ return s
+ try:
+ return s.decode(charset)
+ except LookupError:
+ raise RequestError('unknown charset %r' % charset)
+ except UnicodeDecodeError:
+ raise RequestError('invalid %r encoded string' % charset)
+
def parse_header(line):
"""Parse a Content-type like header.
@@ -74,7 +84,7 @@
'with a "name" parameter: got %r' % full_cdisp)
return (name, cdisp_params.get('filename'))
-def parse_query(qs):
+def parse_query(qs, charset):
"""(qs: string) -> {key:string, string|[string]}
Parse a query given as a string argument and return a dictionary.
@@ -88,6 +98,8 @@
name, value = chunk.split('=', 1)
name = urllib.unquote(name.replace('+', ' '))
value = urllib.unquote(value.replace('+', ' '))
+ name = _decode_string(name, charset)
+ value = _decode_string(value, charset)
_add_field_value(fields, name, value)
return fields
@@ -127,6 +139,8 @@
when handling an exception.
"""
+ DEFAULT_CHARSET = 'iso-8859-1'
+
def __init__(self, stdin, environ):
self.stdin = stdin
self.environ = environ
@@ -165,43 +179,42 @@
def process_inputs(self):
query = self.get_query()
if query:
- self.form.update(parse_query(query))
+ self.form.update(parse_query(query, self.DEFAULT_CHARSET))
length = self.environ.get('CONTENT_LENGTH') or "0"
try:
length = int(length)
except ValueError:
raise RequestError('invalid content-length header')
- content_type = self.environ.get("CONTENT_TYPE")
- if content_type:
- content_type = content_type.split(';')[0]
- if content_type == 'application/x-www-form-urlencoded':
- self._process_urlencoded(length)
- elif content_type == 'multipart/form-data':
- self._process_multipart(length)
+ ctype = self.environ.get("CONTENT_TYPE")
+ if ctype:
+ ctype, ctype_params = parse_header(ctype)
+ if ctype == 'application/x-www-form-urlencoded':
+ self._process_urlencoded(length, ctype_params)
+ elif ctype == 'multipart/form-data':
+ self._process_multipart(length, ctype_params)
- def _process_urlencoded(self, length):
+ def _process_urlencoded(self, length, params):
query = self.stdin.read(length)
if len(query) != length:
raise RequestError('unexpected end of request body')
- self.form.update(parse_query(query))
+ charset = params.get('charset', self.DEFAULT_CHARSET)
+ self.form.update(parse_query(query, charset))
- def _process_multipart(self, length):
- full_ctype = self.environ.get('CONTENT_TYPE')
- (ctype, ctype_params) = parse_header(full_ctype)
- boundary = ctype_params.get('boundary')
- assert ctype == 'multipart/form-data'
+ def _process_multipart(self, length, params):
+ boundary = params.get('boundary')
if not boundary:
raise RequestError('multipart/form-data missing boundary')
+ charset = params.get('charset')
mimeinput = MIMEInput(self.stdin, boundary, length)
try:
for line in mimeinput.readpart():
pass # discard lines up to first boundary
while mimeinput.moreparts():
- self._process_multipart_body(mimeinput)
+ self._process_multipart_body(mimeinput, charset)
except EOFError:
raise RequestError('unexpected end of multipart/form-data')
- def _process_multipart_body(self, mimeinput):
+ def _process_multipart_body(self, mimeinput, charset):
headers = StringIO()
lines = mimeinput.readpart()
for line in lines:
@@ -210,25 +223,30 @@
break
headers.seek(0)
headers = rfc822.Message(headers)
- full_cdisp = headers.get('content-disposition')
- if not full_cdisp:
+ ctype, ctype_params = parse_header(headers.get('content-type', ''))
+ if ctype and 'charset' in ctype_params:
+ charset = ctype_params['charset']
+ cdisp, cdisp_params = parse_header(headers.get('content-disposition',
+ ''))
+ if not cdisp:
raise RequestError('expected Content-Disposition header')
- (cdisp, cdisp_params) = parse_header(full_cdisp)
name = cdisp_params.get('name')
filename = cdisp_params.get('filename')
if not (cdisp == 'form-data' and name):
raise RequestError('expected Content-Disposition: form-data'
- 'with a "name" parameter: got %r' % full_cdisp)
+ 'with a "name" parameter: got %r' %
+ headers.get('content-disposition', ''))
# FIXME: should really to handle Content-Transfer-Encoding and other
# MIME complexity here. See RFC2048 for the full horror story.
if filename:
# it might be large file upload so use a temporary file
- content_type = headers.get('content-type')
- upload = Upload(filename, content_type)
+ upload = Upload(filename, ctype, charset)
upload.receive(lines)
_add_field_value(self.form, name, upload)
else:
- _add_field_value(self.form, name, ''.join(lines))
+ value = _decode_string(''.join(lines),
+ charset or self.DEFAULT_CHARSET)
+ _add_field_value(self.form, name, value)
def get_header(self, name, default=None):
"""get_header(name : string, default : string = None) -> string
@@ -599,9 +617,11 @@
content_type
the content type provided by the user-agent in the request
that uploaded this file.
+ charset
+ the charset provide by the user-agent
"""
- def __init__(self, orig_filename, content_type=None):
+ def __init__(self, orig_filename, content_type=None, charset=None):
if orig_filename:
self.orig_filename = orig_filename
bspos = orig_filename.rfind("\\")
@@ -621,6 +641,7 @@
self.orig_filename = None
self.base_filename = None
self.content_type = content_type
+ self.charset = charset
self.fp = None
def receive(self, lines):
--Apple-Mail-5--531733632--