1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-02-16 19:47:40 +02:00

All: Fixes #209: Items with non-ASCII characters end up truncated on Nextcloud

This commit is contained in:
Laurent Cozic 2018-02-12 18:15:22 +00:00
parent 995d8c35dd
commit 7841421c0d
4 changed files with 42 additions and 33 deletions

View File

@ -60,6 +60,7 @@ async function allSyncTargetItemsEncrypted() {
}
async function localItemsSameAsRemote(locals, expect) {
let error = null;
try {
let files = await fileApi().list();
files = files.items;
@ -81,12 +82,15 @@ async function localItemsSameAsRemote(locals, expect) {
// }
let remoteContent = await fileApi().get(path);
remoteContent = dbItem.type_ == BaseModel.TYPE_NOTE ? await Note.unserialize(remoteContent) : await Folder.unserialize(remoteContent);
expect(remoteContent.title).toBe(dbItem.title);
}
} catch (error) {
console.error(error);
} catch (e) {
error = e;
}
expect(error).toBe(null);
}
let insideBeforeEach = false;
@ -985,4 +989,15 @@ describe('Synchronizer', function() {
expect(resource1.encryption_blob_encrypted).toBe(0);
}));
it('should create remote items with UTF-8 content', asyncTest(async () => {
let folder = await Folder.save({ title: "Fahrräder" });
await Note.save({ title: "Fahrräder", body: "Fahrräder", parent_id: folder.id });
let all = await allItems();
await synchronizer().start();
await localItemsSameAsRemote(all, expect);
}));
});

View File

@ -158,6 +158,22 @@ class WebDavApi {
return this.exec('PROPFIND', path, body, { 'Depth': depth }, options);
}
requestToCurl_(url, options) {
let output = [];
output.push('curl');
if (options.method) output.push('-X ' + options.method);
if (options.headers) {
for (let n in options.headers) {
if (!options.headers.hasOwnProperty(n)) continue;
output.push('-H ' + '"' + n + ': ' + options.headers[n] + '"');
}
}
if (options.body) output.push('--data ' + "'" + options.body + "'");
output.push(url);
return output.join(' ');
}
// curl -u admin:123456 'http://nextcloud.local/remote.php/dav/files/admin/' -X PROPFIND --data '<?xml version="1.0" encoding="UTF-8"?>
// <d:propfind xmlns:d="DAV:">
// <d:prop xmlns:oc="http://owncloud.org/ns">
@ -175,7 +191,10 @@ class WebDavApi {
if (authToken) headers['Authorization'] = 'Basic ' + authToken;
if (typeof body === 'string') headers['Content-Length'] = body.length;
// /!\ Doesn't work with UTF-8 strings as it results in truncated content. Content-Length
// /!\ should not be needed anyway, but was required by one service. If re-implementing this
// /!\ test with various content, including binary blobs.
// if (typeof body === 'string') headers['Content-Length'] = body.length;
const fetchOptions = {};
fetchOptions.headers = headers;
@ -188,6 +207,7 @@ class WebDavApi {
let response = null;
// console.info('WebDAV Call', method + ' ' + url, headers, options);
// console.info(this.requestToCurl_(url, fetchOptions));
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
response = await shim.uploadBlob(url, fetchOptions);

View File

@ -15,7 +15,6 @@ shared.checkSyncConfig = async function(comp, settings) {
const options = Setting.subValues('sync.' + syncTargetId, settings);
comp.setState({ checkSyncConfigResult: 'checking' });
const result = await SyncTargetClass.checkConfig(options);
console.info(result);
comp.setState({ checkSyncConfigResult: result });
}

View File

@ -42,8 +42,8 @@ class FileApiDriverWebDav {
const isCollection = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:resourcetype', 0, 'd:collection', 0]);
const lastModifiedString = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getlastmodified', 0]);
const sizeDONOTUSE = Number(this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getcontentlength', 0]));
if (isNaN(sizeDONOTUSE)) throw new Error('Cannot get content size: ' + JSON.stringify(resource));
// const sizeDONOTUSE = Number(this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getcontentlength', 0]));
// if (isNaN(sizeDONOTUSE)) throw new Error('Cannot get content size: ' + JSON.stringify(resource));
if (!lastModifiedString) throw new Error('Could not get lastModified date: ' + JSON.stringify(resource));
@ -55,7 +55,7 @@ class FileApiDriverWebDav {
// created_time: lastModifiedDate.getTime(),
updated_time: lastModifiedDate.getTime(),
isDir: isCollection === '',
sizeDONOTUSE: sizeDONOTUSE, // This property is used only for the WebDAV PUT hack (see below) so mark it as such so that it can be removed with the hack later on.
// sizeDONOTUSE: sizeDONOTUSE, // This property is used only for the WebDAV PUT hack (see below) so mark it as such so that it can be removed with the hack later on.
};
}
@ -304,32 +304,7 @@ class FileApiDriverWebDav {
}
async put(path, content, options = null) {
// In theory, if a client doesn't complete an upload, the file will not appear in the Nextcloud app. Likewise if
// the server interrupts the upload midway, the client should receive some kind of error and try uploading the
// file again next time. At the very least the file should not appear half-uploaded on the server. In practice
// however it seems some files might end up half uploaded on the server (at least on ocloud.de) so, for now,
// instead of doing a simple PUT, we do it to a temp file on Nextcloud, then check the file size and, if it
// matches, move it its actual place (hoping the server won't mess up and only copy half of the file).
// This is innefficient so once the bug is better understood it should hopefully be possible to go back to
// using a single PUT call.
let contentSize = 0;
if (content) contentSize = content.length;
if (options && options.path) {
const stat = await shim.fsDriver().stat(options.path);
contentSize = stat.size;
}
const tempPath = this.fileApi_.tempDirName() + '/' + basename(path) + '_' + Date.now();
await this.api().exec('PUT', tempPath, content, null, options);
const stat = await this.stat(tempPath);
if (stat.sizeDONOTUSE != contentSize) {
// await this.delete(tempPath);
throw new Error('WebDAV PUT - Size check failed for ' + tempPath + ' Expected: ' + contentSize + '. Found: ' + stat.sizeDONOTUSE);
}
await this.move(tempPath, path);
return await this.api().exec('PUT', path, content, null, options);
}
async delete(path) {