Skip to content

Commit

Permalink
use RFC5987 encoding for filenames
Browse files Browse the repository at this point in the history
as described in RFC 6266 describing Content-Disposition
  • Loading branch information
minrk committed Aug 16, 2017
1 parent 64ed6e4 commit 3633065
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 13 deletions.
16 changes: 15 additions & 1 deletion notebook/base/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,20 @@ def set_default_headers(self):
if self.allow_credentials:
self.set_header("Access-Control-Allow-Credentials", 'true')

def set_attachment_header(self, filename):
"""Set Content-Disposition: attachment header
As a method to ensure handling of filename encoding
"""
escaped_filename = url_escape(filename)
self.set_header('Content-Disposition',
'attachment;'
' filename*= UTF-8''{utf8}'
.format(
utf8=escaped_filename,
)
)

def get_origin(self):
# Handle WebSocket Origin naming convention differences
# The difference between version 8 and 13 is that in 8 the
Expand Down Expand Up @@ -478,7 +492,7 @@ class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
def get(self, path):
if os.path.splitext(path)[1] == '.ipynb' or self.get_argument("download", False):
name = path.rsplit('/', 1)[-1]
self.set_header('Content-Disposition','attachment; filename="%s"' % name)
self.set_attachment_header(name)

return web.StaticFileHandler.get(self, path)

Expand Down
7 changes: 3 additions & 4 deletions notebook/bundler/tarball_bundler.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ def bundle(handler, model):
with io.BytesIO() as tar_buffer:
with tarfile.open(tar_filename, "w:gz", fileobj=tar_buffer) as tar:
tar.addfile(info, io.BytesIO(notebook_content))

handler.set_header('Content-Disposition',
'attachment; filename="{}"'.format(tar_filename))

handler.set_attachment_header(tar_filename)
handler.set_header('Content-Type', 'application/gzip')

# Return the buffer value as the response
handler.finish(tar_buffer.getvalue())
3 changes: 1 addition & 2 deletions notebook/bundler/zip_bundler.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ def bundle(handler, model):

# Headers
zip_filename = os.path.splitext(notebook_name)[0] + '.zip'
handler.set_header('Content-Disposition',
'attachment; filename="%s"' % zip_filename)
handler.set_attachment_header(zip_filename)
handler.set_header('Content-Type', 'application/zip')

# Get associated files
Expand Down
2 changes: 1 addition & 1 deletion notebook/files/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get(self, path, include_body=True):
model = cm.get(path, type='file', content=include_body)

if self.get_argument("download", False):
self.set_header('Content-Disposition','attachment; filename="%s"' % name)
self.set_attachment_header(name)

# get mimetype from filename
if name.endswith('.ipynb'):
Expand Down
6 changes: 2 additions & 4 deletions notebook/nbconvert/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ def respond_zip(handler, name, output, resources):

# Headers
zip_filename = os.path.splitext(name)[0] + '.zip'
handler.set_header('Content-Disposition',
'attachment; filename="%s"' % zip_filename)
handler.set_attachment_header(zip_filename)
handler.set_header('Content-Type', 'application/zip')

# Prepare the zip file
Expand Down Expand Up @@ -114,8 +113,7 @@ def get(self, format, path):
# Force download if requested
if self.get_argument('download', 'false').lower() == 'true':
filename = os.path.splitext(name)[0] + resources['output_extension']
self.set_header('Content-Disposition',
'attachment; filename="%s"' % filename)
self.set_attachment_header(filename)

# MIME type
if exporter.output_mimetype:
Expand Down
2 changes: 1 addition & 1 deletion notebook/tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def test_download(self):
r = self.request('GET', 'files/test.txt?download=1')
disposition = r.headers.get('Content-Disposition', '')
self.assertIn('attachment', disposition)
self.assertIn('filename="test.txt"', disposition)
self.assertIn('filename*= UTF-8''test.txt', disposition)

def test_view_html(self):
nbdir = self.notebook_dir
Expand Down

0 comments on commit 3633065

Please sign in to comment.