diff --git a/packages/app-cli/tests/EnexToMd.ts b/packages/app-cli/tests/EnexToMd.ts index 35d5d914d..0579b736b 100644 --- a/packages/app-cli/tests/EnexToMd.ts +++ b/packages/app-cli/tests/EnexToMd.ts @@ -113,4 +113,18 @@ describe('EnexToMd', function() { expect(all[0].body).toBe(''); }); + it('should handle invalid mime types', async () => { + // This is to handle the case where a resource has an invalid mime type, + // but that type can be determined from the filename. For example, in + // this thread, the ENEX file contains a "file.zip" file with a mime + // type "application/octet-stream", which can later cause problems to + // open the file. + // https://discourse.joplinapp.org/t/importing-a-note-with-a-zip-file/12123?u=laurent + const filePath = `${enexSampleBaseDir}/WithInvalidMime.enex`; + await importEnex('', filePath); + const all = await Resource.all(); + expect(all.length).toBe(1); + expect(all[0].mime).toBe('application/zip'); + }); + }); diff --git a/packages/app-cli/tests/enex_to_md/WithInvalidMime.enex b/packages/app-cli/tests/enex_to_md/WithInvalidMime.enex new file mode 100644 index 000000000..8b2a94b13 --- /dev/null +++ b/packages/app-cli/tests/enex_to_md/WithInvalidMime.enex @@ -0,0 +1,53 @@ + + + +WithInvalidMime

]]>
20201218T233549Z20201218T233706Z51.575164794921880.228172031501373429.71427536010742laurent@cozic.netdesktop.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 +application/octet-stream00019700101T000000Zphoto.zip
+
diff --git a/packages/lib/import-enex.js b/packages/lib/import-enex.js index 4eda75be5..7ea2a1a6b 100644 --- a/packages/lib/import-enex.js +++ b/packages/lib/import-enex.js @@ -14,6 +14,7 @@ const md5 = require('md5'); const { Base64Decode } = require('base64-stream'); const md5File = require('md5-file'); const shim = require('./shim').default; +const { mime } = require('./mime-utils'); // const Promise = require('promise'); const fs = require('fs-extra'); @@ -514,11 +515,27 @@ function importEnex(parentFolderId, filePath, importOptions = null) { noteAttributes = null; } else if (n == 'resource') { + let mimeType = noteResource.mime ? noteResource.mime.trim() : ''; + + // Evernote sometimes gives an invalid or generic + // "application/octet-stream" mime type for files that could + // have a valid mime type, based on the extension. So in + // general, we trust the filename more than the provided mime + // type. + // https://discourse.joplinapp.org/t/importing-a-note-with-a-zip-file/12123 + if (noteResource.filename) { + const mimeTypeFromFile = mime.fromFilename(noteResource.filename); + if (mimeTypeFromFile && mimeTypeFromFile !== mimeType) { + importOptions.onError(new Error(`Invalid mime type "${mimeType}" for resource "${noteResource.filename}". Using "${mimeTypeFromFile}" instead.`)); + mimeType = mimeTypeFromFile; + } + } + note.resources.push({ id: noteResource.id, dataFilePath: noteResource.dataFilePath, dataEncoding: noteResource.dataEncoding, - mime: noteResource.mime ? noteResource.mime.trim() : '', + mime: mimeType, title: noteResource.filename ? noteResource.filename.trim() : '', filename: noteResource.filename ? noteResource.filename.trim() : '', hasData: noteResource.hasData,