-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
fix incorrect encoding of filenames with spaces in #2090
Changes from 6 commits
d833855
70c8861
10131f7
918920a
c758d36
d1967f8
c403033
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Fix a bug where media with spaces in the name would get a corrupted name. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,10 +100,29 @@ def _quote(x): | |
|
||
request.setHeader(b"Content-Type", media_type.encode("UTF-8")) | ||
if upload_name: | ||
if is_ascii(upload_name): | ||
disposition = "inline; filename=%s" % (_quote(upload_name),) | ||
# RFC6266 section 4.1 [1] defines both `filename` and `filename*`. | ||
# | ||
# `filename` is defined to be a `value`, which is defined by RFC2616 | ||
# section 3.6 [2] to be a `token` or a `quoted-string`, where a `token` | ||
# is (essentially) a single US-ASCII word, and a `quoted-string` is a | ||
# US-ASCII string surrounded by double-quotes, using backslash as an | ||
# escape charater. Note that %-encoding is *not* permitted. | ||
# | ||
# `filename*` is defined to be an `ext-value`, which is defined in | ||
# RFC5987 section 3.2.1 [3] to be `charset "'" [ language ] "'" value-chars`, | ||
# where `value-chars` is essentially a %-encoded string in the given charset. | ||
# | ||
# [1]: https://tools.ietf.org/html/rfc6266#section-4.1 | ||
# [2]: https://tools.ietf.org/html/rfc2616#section-3.6 | ||
# [3]: https://tools.ietf.org/html/rfc5987#section-3.2.1 | ||
|
||
# We avoid the quoted-string version of filename, because (a) synapse didn't | ||
# correctly interpret those as of 0.99.2 and (b) they are a bit of a pain and we | ||
# may as well just do the filename* version. | ||
if _can_encode_filename_as_token(upload_name): | ||
disposition = 'inline; filename=%s' % (upload_name, ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not do filename* everywhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the main thinking is that some things won't have good support for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean good support for okay, fair enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry, yes that's what I mean. |
||
else: | ||
disposition = "inline; filename*=utf-8''%s" % (_quote(upload_name),) | ||
disposition = "inline; filename*=utf-8''%s" % (_quote(upload_name), ) | ||
|
||
request.setHeader(b"Content-Disposition", disposition.encode('ascii')) | ||
|
||
|
@@ -116,6 +135,35 @@ def _quote(x): | |
request.setHeader(b"Content-Length", b"%d" % (file_size,)) | ||
|
||
|
||
# separators as defined in RFC2616. SP and HT are handled separately. | ||
# see _can_encode_filename_as_token. | ||
_FILENAME_SEPARATOR_CHARS = set(( | ||
"(", ")", "<", ">", "@", ",", ";", ":", "\\", '"', | ||
"/", "[", "]", "?", "=", "{", "}", | ||
)) | ||
|
||
|
||
def _can_encode_filename_as_token(x): | ||
for c in x: | ||
# from RFC2616: | ||
# | ||
# token = 1*<any CHAR except CTLs or separators> | ||
# | ||
# separators = "(" | ")" | "<" | ">" | "@" | ||
# | "," | ";" | ":" | "\" | <"> | ||
# | "/" | "[" | "]" | "?" | "=" | ||
# | "{" | "}" | SP | HT | ||
# | ||
# CHAR = <any US-ASCII character (octets 0 - 127)> | ||
# | ||
# CTL = <any US-ASCII control character | ||
# (octets 0 - 31) and DEL (127)> | ||
# | ||
if ord(c) >= 127 or ord(c) <= 32 or c in _FILENAME_SEPARATOR_CHARS: | ||
return False | ||
return True | ||
|
||
|
||
@defer.inlineCallbacks | ||
def respond_with_responder(request, responder, media_type, file_size, upload_name=None): | ||
"""Responds to the request with given responder. If responder is None then | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd suggest saying `filename` here, to make it clear you're talking about `filename` rather than `filename*`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
point, done