Compare commits

...
7 changed files with 80 additions and 20 deletions

No files matched your search

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export4.dtd">
<en-export export-date="20230920T212153Z" application="Evernote" version="10.60.4">
<note>
<title>ABOUT</title>
<created>20200221T010525Z</created>
<updated>20230720T000138Z</updated>
<tag>recovery</tag>
<tag>San Luis Obispo</tag>
<tag>homelessness in SLO</tag>
<tag>Sunny Acres</tag>
<note-attributes>
<author>Michael B. Goldstein</author>
<source>web.clip</source>
<source-url>https://www.sunnyacres.community/about.html</source-url>
</note-attributes>
<content>
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note></en-note> ]]>
</content>
<resource>
<data encoding="base64">
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 truncated
</data>
<mime>image/png</mime>
<width>16</width>
<height>16</height>
<resource-attributes>
<file-name>08.06.2014 16:58:55</file-name>
<source-url>en-cache://tokenKey%3D%22AuthToken%3AUser%3A33065190%22+542a52b5-e793-4e26-9839-c13552e5a75a+2fb6ad590ae59702499393cc35850cdf+https://www.evernote.com/shard/s254/res/6da89c4b-e2d2-41a5-afe7-df2c15839e6f</source-url>
</resource-attributes>
</resource>
</note>
</en-export>
@@ -1,4 +1,5 @@
<table>
<div></div> <!-- INVALID! -->
<tr>
<td>one</td>
<td>two</td>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.13.10",
"version": "2.13.12",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,
+29 -13
View File
@@ -142,20 +142,28 @@ describe('import-enex-md-gen', () => {
expect(all[0].mime).toBe('application/zip');
});
it('should keep importing notes when one of them is corrupted', async () => {
const filePath = `${enexSampleBaseDir}/ImportTestCorrupt.enex`;
const errors: any[] = [];
await importEnex('', filePath, {
onError: (error: any) => errors.push(error),
});
const notes = await Note.all();
expect(notes.length).toBe(2);
// Disabled for now because the ENEX parser has become so error-tolerant
// that it's no longer possible to generate a note that would generate a
// failure.
// it('should keep importing notes when one of them is corrupted', async () => {
// const filePath = `${enexSampleBaseDir}/ImportTestCorrupt.enex`;
// const errors: any[] = [];
// const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(jest.fn());
// await importEnex('', filePath, {
// onError: (error: any) => errors.push(error),
// });
// consoleSpy.mockRestore();
// const notes:NoteEntity[] = await Note.all();
// expect(notes.length).toBe(2);
// expect(notes.find(n => n.title === 'Note 1')).toBeTruthy();
// expect(notes.find(n => n.title === 'Note 3')).toBeTruthy();
// Check that an error was recorded and that it includes the title
// of the note, so that it can be found back by the user
expect(errors.length).toBe(1);
expect(errors[0].message.includes('Note 2')).toBe(true);
});
// // Check that an error was recorded and that it includes the title
// // of the note, so that it can be found back by the user
// expect(errors.length).toBe(1);
// expect(errors[0].message.includes('Note 2')).toBe(true);
// });
it('should throw an error and stop if the outer XML is invalid', async () => {
await expectThrow(async () => importEnexFile('invalid_html.enex'));
@@ -204,4 +212,12 @@ describe('import-enex-md-gen', () => {
expect(resource.title).toBe('app_images/resizable/961b875f-24ac-402f-9b76-37e2d4f03a6c/house_500.jpg.png');
});
it('should sanitize resource filenames with colons', async () => {
await importEnexFile('resource_filename_with_colons.enex');
const resource: ResourceEntity = (await Resource.all())[0];
expect(resource.filename).toBe('08.06.2014165855');
expect(resource.file_extension).toBe('2014165855');
expect(resource.title).toBe('08.06.2014 16:58:55');
});
});
+9 -4
View File
@@ -1239,6 +1239,14 @@ function drawTable(table: Section) {
continue;
}
if (typeof tr === 'string') {
// A <TABLE> tag should only have <TR> tags as direct children.
// However certain Evernote notes can contain other random tags
// such as empty DIVs. In that case we just skip the content.
// See test "table_with_invalid_content.html".
continue;
}
const isHeader = tr.isHeader;
const line = [];
const headerLine = [];
@@ -1247,10 +1255,7 @@ function drawTable(table: Section) {
const td = tr.lines[tdIndex];
if (typeof td === 'string') {
// A <TR> tag should only have <TD> tags as direct children.
// However certain Evernote notes can contain other random tags
// such as empty DIVs. In that case we just skip the content.
// See test "table_with_invalid_content.html".
// Same comment as above the <TR> tags.
continue;
}
+2 -2
View File
@@ -9,7 +9,7 @@ import shim from './shim';
import { NoteEntity, ResourceEntity } from './services/database/types';
import { enexXmlToMd } from './import-enex-md-gen';
import { MarkupToHtml } from '@joplin/renderer';
import { fileExtension, friendlySafeFilename } from './path-utils';
import { fileExtension, friendlySafeFilename, safeFileExtension } from './path-utils';
const moment = require('moment');
const { wrapError } = require('./errorUtils');
const { enexXmlToHtml } = require('./import-enex-html-gen.js');
@@ -208,7 +208,7 @@ async function saveNoteResources(note: ExtractedNote) {
delete (toSave as any).dataFilePath;
delete (toSave as any).dataEncoding;
delete (toSave as any).hasData;
toSave.file_extension = resource.filename ? fileExtension(resource.filename) : '';
toSave.file_extension = resource.filename ? safeFileExtension(fileExtension(resource.filename)) : '';
// ENEX resource filenames can contain slashes, which may confuse other
// parts of the app, which expect this `filename` field to be safe.
+3
View File
@@ -39,6 +39,9 @@ export function isHidden(path: string) {
return b[0] === '.';
}
// Note that this function only sanitizes a file extension - it does NOT extract
// the file extension from a filename. So the way you'd normally call this is
// `safeFileExtension(fileExtension(filename))`
export function safeFileExtension(e: string, maxLength: number = null) {
// In theory the file extension can have any length but in practice Joplin
// expects a fixed length, so we limit it to 20 which should cover most cases.