From 3d8905207ec1c4b4a39d7bb71c01ab7ad333d226 Mon Sep 17 00:00:00 2001 From: azivner Date: Sun, 13 Jan 2019 10:22:17 +0100 Subject: [PATCH] fixed export with non-ASCII characters in note title, fixes #285, #331 --- src/routes/api/file_upload.js | 3 ++- src/services/export/opml.js | 11 +++++------ src/services/export/single.js | 8 +++----- src/services/export/tar.js | 6 +++--- src/services/utils.js | 21 ++++++++++++++++++++- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/routes/api/file_upload.js b/src/routes/api/file_upload.js index 1249bea7c..d13e23edb 100644 --- a/src/routes/api/file_upload.js +++ b/src/routes/api/file_upload.js @@ -3,6 +3,7 @@ const noteService = require('../../services/notes'); const protectedSessionService = require('../../services/protected_session'); const repository = require('../../services/repository'); +const utils = require('../../services/utils'); async function uploadFile(req) { const parentNoteId = req.params.parentNoteId; @@ -49,7 +50,7 @@ async function downloadFile(req, res) { const originalFileName = await note.getLabel('originalFileName'); const fileName = originalFileName ? originalFileName.value : note.title; - res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"'); + res.setHeader('Content-Disposition', utils.getContentDisposition(fileName)); res.setHeader('Content-Type', note.mime); res.send(note.content); diff --git a/src/services/export/opml.js b/src/services/export/opml.js index a21907ce7..b00ed9340 100644 --- a/src/services/export/opml.js +++ b/src/services/export/opml.js @@ -1,13 +1,10 @@ "use strict"; -const sanitize = require("sanitize-filename"); -const repository = require("../../services/repository"); -const utils = require('../../services/utils'); +const repository = require("../repository"); +const utils = require('../utils'); async function exportToOpml(branch, res) { const note = await branch.getNote(); - const title = (branch.prefix ? (branch.prefix + ' - ') : '') + note.title; - const sanitizedTitle = sanitize(title); async function exportNoteInner(branchId) { const branch = await repository.getBranch(branchId); @@ -31,7 +28,9 @@ async function exportToOpml(branch, res) { res.write(''); } - res.setHeader('Content-Disposition', 'file; filename="' + sanitizedTitle + '.opml"'); + const filename = (branch.prefix ? (branch.prefix + ' - ') : '') + note.title + ".opml"; + + res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); res.setHeader('Content-Type', 'text/x-opml'); res.write(` diff --git a/src/services/export/single.js b/src/services/export/single.js index b0e0ac612..7a47abc21 100644 --- a/src/services/export/single.js +++ b/src/services/export/single.js @@ -1,9 +1,9 @@ "use strict"; -const sanitize = require("sanitize-filename"); const TurndownService = require('turndown'); const mimeTypes = require('mime-types'); const html = require('html'); +const utils = require('../utils'); async function exportSingleNote(branch, format, res) { const note = await branch.getNote(); @@ -42,11 +42,9 @@ async function exportSingleNote(branch, format, res) { mime = 'application/json'; } - const name = sanitize(note.title); + const filename = note.title + "." + extension; - console.log(name, extension, mime); - - res.setHeader('Content-Disposition', `file; filename="${name}.${extension}"`); + res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); res.setHeader('Content-Type', mime + '; charset=UTF-8'); res.send(payload); diff --git a/src/services/export/tar.js b/src/services/export/tar.js index a36af7695..5e0cffdf6 100644 --- a/src/services/export/tar.js +++ b/src/services/export/tar.js @@ -4,10 +4,10 @@ const html = require('html'); const repository = require('../repository'); const tar = require('tar-stream'); const path = require('path'); -const sanitize = require("sanitize-filename"); const mimeTypes = require('mime-types'); const TurndownService = require('turndown'); const packageInfo = require('../../../package.json'); +const utils = require('../utils'); /** * @param format - 'html' or 'markdown' @@ -219,9 +219,9 @@ async function exportToTar(branch, format, res) { pack.finalize(); const note = await branch.getNote(); - const tarFileName = sanitize((branch.prefix ? (branch.prefix + " - ") : "") + note.title); + const tarFileName = (branch.prefix ? (branch.prefix + " - ") : "") + note.title + ".tar"; - res.setHeader('Content-Disposition', `file; filename="${tarFileName}.tar"`); + res.setHeader('Content-Disposition', utils.getContentDisposition(tarFileName)); res.setHeader('Content-Type', 'application/tar'); pack.pipe(res); diff --git a/src/services/utils.js b/src/services/utils.js index 2f1884abe..aff4375e0 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -4,6 +4,7 @@ const crypto = require('crypto'); const randtoken = require('rand-token').generator({source: 'crypto'}); const unescape = require('unescape'); const escape = require('escape-html'); +const sanitize = require("sanitize-filename"); function newEntityId() { return randomString(12); @@ -127,6 +128,22 @@ function crash() { } } +function sanitizeFilenameForHeader(filename) { + let sanitizedFilename = sanitize(filename); + + if (sanitizedFilename.trim().length === 0) { + sanitizedFilename = "file"; + } + + return encodeURIComponent(sanitizedFilename) +} + +function getContentDisposition(filename) { + const sanitizedFilename = sanitizeFilenameForHeader(filename); + + return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`; +} + module.exports = { randomSecureToken, randomString, @@ -147,5 +164,7 @@ module.exports = { intersection, union, escapeRegExp, - crash + crash, + sanitizeFilenameForHeader, + getContentDisposition }; \ No newline at end of file