diff --git a/CliClient/tests/synchronizer.js b/CliClient/tests/synchronizer.js index 9cbd1b129..2e6c670ff 100644 --- a/CliClient/tests/synchronizer.js +++ b/CliClient/tests/synchronizer.js @@ -1034,6 +1034,33 @@ describe('Synchronizer', function() { expect(allEncrypted).toBe(false); })); + it('should set the resource file size after decryption', asyncTest(async () => { + Setting.setValue('encryption.enabled', true); + const masterKey = await loadEncryptionMasterKey(); + + let folder1 = await Folder.save({ title: "folder1" }); + let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id }); + await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg'); + let resource1 = (await Resource.all())[0]; + await Resource.setFileSizeOnly(resource1.id, -1); + let resourcePath1 = Resource.fullPath(resource1); + await synchronizer().start(); + + await switchClient(2); + + await synchronizer().start(); + Setting.setObjectKey('encryption.passwordCache', masterKey.id, '123456'); + await encryptionService().loadMasterKeysFromSettings(); + + const fetcher = new ResourceFetcher(() => { return synchronizer().api() }); + fetcher.queueDownload(resource1.id); + await fetcher.waitForAllFinished(); + await decryptionWorker().start(); + + const resource1_2 = await Resource.load(resource1.id); + expect(resource1_2.size).toBe(2720); + })); + it('should encrypt remote resources after encryption has been enabled', asyncTest(async () => { while (insideBeforeEach) await time.msleep(100); diff --git a/ReactNativeClient/ios/Joplin/Info.plist b/ReactNativeClient/ios/Joplin/Info.plist index f96b7c737..037989214 100644 --- a/ReactNativeClient/ios/Joplin/Info.plist +++ b/ReactNativeClient/ios/Joplin/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 10.0.31 + 10.0.32 CFBundleSignature ???? CFBundleVersion - 31 + 32 LSRequiresIPhoneOS NSAppTransportSecurity diff --git a/ReactNativeClient/lib/models/Resource.js b/ReactNativeClient/lib/models/Resource.js index 2219fbcdc..7427ec976 100644 --- a/ReactNativeClient/lib/models/Resource.js +++ b/ReactNativeClient/lib/models/Resource.js @@ -6,6 +6,7 @@ const Setting = require('lib/models/Setting.js'); const ArrayUtils = require('lib/ArrayUtils.js'); const pathUtils = require('lib/path-utils.js'); const { mime } = require('lib/mime-utils.js'); +const { shim } = require('lib/shim'); const { filename, safeFilename } = require('lib/path-utils.js'); const { FsDriverDummy } = require('lib/fs-driver-dummy.js'); const markdownUtils = require('lib/markdownUtils'); @@ -214,6 +215,10 @@ class Resource extends BaseItem { await ResourceLocalState.save(Object.assign({}, state, { resource_id: id })); } + static async needFileSizeSet() { + return this.modelSelectAll('SELECT * FROM resources WHERE `size` < 0 AND encryption_blob_encrypted = 0'); + } + // Only set the `size` field and nothing else, not even the update_time // This is because it's only necessary to do it once after migration 20 // and each client does it so there's no need to sync the resource. diff --git a/ReactNativeClient/lib/services/DecryptionWorker.js b/ReactNativeClient/lib/services/DecryptionWorker.js index 93f947550..b5ec01cbe 100644 --- a/ReactNativeClient/lib/services/DecryptionWorker.js +++ b/ReactNativeClient/lib/services/DecryptionWorker.js @@ -1,5 +1,6 @@ const BaseItem = require('lib/models/BaseItem'); const Resource = require('lib/models/Resource'); +const ResourceService = require('lib/services/ResourceService'); const { Logger } = require('lib/logger.js'); class DecryptionWorker { @@ -132,6 +133,10 @@ class DecryptionWorker { throw error; } + // 2019-05-12: Temporary to set the file size of the resources + // that weren't set in migration/20.js due to being on the sync target + await ResourceService.autoSetFileSizes(); + this.logger().info('DecryptionWorker: completed decryption.'); this.dispatchReport({ state: 'idle' }); diff --git a/ReactNativeClient/lib/services/ResourceFetcher.js b/ReactNativeClient/lib/services/ResourceFetcher.js index dbcfc9a04..f53e28c62 100644 --- a/ReactNativeClient/lib/services/ResourceFetcher.js +++ b/ReactNativeClient/lib/services/ResourceFetcher.js @@ -1,5 +1,6 @@ const Resource = require('lib/models/Resource'); const BaseService = require('lib/services/BaseService'); +const ResourceService = require('lib/services/ResourceService'); const BaseSyncTarget = require('lib/BaseSyncTarget'); const { Logger } = require('lib/logger.js'); const EventEmitter = require('events'); @@ -103,14 +104,17 @@ class ResourceFetcher extends BaseService { // 2019-05-12: This is only necessary to set the file size of the resources that come via // sync. The other ones have been done using migrations/20.js. This code can be removed // after a few months. - if (resource.size < 0 && localResourceContentPath) { - const itDoes = await shim.fsDriver().waitTillExists(localResourceContentPath); - const fileStat = await shim.fsDriver().stat(localResourceContentPath); - await Resource.setFileSizeOnly(resource.id, fileStat.size); + if (resource.size < 0 && localResourceContentPath && !resource.encryption_blob_encrypted) { + await ResourceService.autoSetFileSizes(); } delete this.fetchingItems_[resource.id]; this.scheduleQueueProcess(); + + // Note: This downloadComplete event is not really right or useful because the resource + // might still be encrypted and the caller usually can't do much with this. In particular + // the note being displayed will refresh the resource images but since they are still + // encrypted it's not useful. Probably, the views should listen to DecryptionWorker events instead. if (emitDownloadComplete) this.eventEmitter_.emit('downloadComplete', { id: resource.id }); this.updateReport(); } diff --git a/ReactNativeClient/lib/services/ResourceService.js b/ReactNativeClient/lib/services/ResourceService.js index 153156296..219af8de9 100644 --- a/ReactNativeClient/lib/services/ResourceService.js +++ b/ReactNativeClient/lib/services/ResourceService.js @@ -111,6 +111,20 @@ class ResourceService extends BaseService { } } + static async autoSetFileSize(resourceId, filePath) { + const itDoes = await shim.fsDriver().waitTillExists(filePath); + const fileStat = await shim.fsDriver().stat(filePath); + await Resource.setFileSizeOnly(resourceId, fileStat.size); + } + + static async autoSetFileSizes() { + const resources = await Resource.needFileSizeSet(); + + for (const r of resources) { + await this.autoSetFileSize(r.id, Resource.fullPath(r)); + } + } + async maintenance() { await this.indexNoteResources(); await this.deleteOrphanResources();