You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	All: Fixes #2257: Prevent decryption loop when a resource cannot be decrypted
This commit is contained in:
		| @@ -237,7 +237,6 @@ async function setupDatabaseAndSynchronizer(id = null) { | ||||
| 		syncTarget.setFileApi(fileApi()); | ||||
| 		syncTarget.setLogger(logger); | ||||
| 		synchronizers_[id] = await syncTarget.synchronizer(); | ||||
| 		synchronizers_[id].autoStartDecryptionWorker_ = false; // For testing we disable this since it would make the tests non-deterministic | ||||
| 	} | ||||
|  | ||||
| 	encryptionServices_[id] = new EncryptionService(); | ||||
|   | ||||
| @@ -3,6 +3,7 @@ const Tag = require('lib/models/Tag'); | ||||
| const Note = require('lib/models/Note'); | ||||
| const { reg } = require('lib/registry.js'); | ||||
| const ResourceFetcher = require('lib/services/ResourceFetcher'); | ||||
| const DecryptionWorker = require('lib/services/DecryptionWorker'); | ||||
|  | ||||
| const reduxSharedMiddleware = async function(store, next, action) { | ||||
| 	const newState = store.getState(); | ||||
| @@ -21,6 +22,17 @@ const reduxSharedMiddleware = async function(store, next, action) { | ||||
| 		ResourceFetcher.instance().autoAddResources(); | ||||
| 	} | ||||
|  | ||||
| 	// In general the DecryptionWorker is started via events, such as when an encrypted note | ||||
| 	// is received via sync, or after an encrypted has been downloaded. However, in some cases, | ||||
| 	// in particular when an item cannot be decrypted, the service won't retry automatically, | ||||
| 	// since it's not useful because the data most likely is corrupted. In some | ||||
| 	// cases the user might want to retry anyway, so we enable this by starting the service | ||||
| 	// automatically after each full sync (which is triggered when the user presses the sync | ||||
| 	// button, but not when a note is saved). | ||||
| 	if (action.type === 'SYNC_COMPLETED' && action.isFullSync) { | ||||
| 		DecryptionWorker.instance().scheduleStart(); | ||||
| 	} | ||||
|  | ||||
| 	if (action.type == 'NOTE_DELETE' || | ||||
| 		action.type == 'NOTE_UPDATE_ONE' || | ||||
| 		action.type == 'NOTE_UPDATE_ALL' || | ||||
|   | ||||
| @@ -262,11 +262,18 @@ class Resource extends BaseItem { | ||||
| 		await this.db().exec('INSERT INTO resources_to_download (resource_id, updated_time, created_time) SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM resources_to_download WHERE resource_id = ?)', [resourceId, t, t, resourceId]); | ||||
| 	} | ||||
|  | ||||
| 	static async downloadedButEncryptedBlobCount() { | ||||
| 	static async downloadedButEncryptedBlobCount(excludedIds = null) { | ||||
| 		let excludedSql = ''; | ||||
| 		if (excludedIds && excludedIds.length) { | ||||
| 			excludedSql = `AND resource_id NOT IN ("${excludedIds.join('","')}")`; | ||||
| 		} | ||||
|  | ||||
| 		const r = await this.db().selectOne(` | ||||
| 			SELECT count(*) as total | ||||
| 			FROM resource_local_states | ||||
| 			WHERE fetch_status = ? AND resource_id IN (SELECT id FROM resources WHERE encryption_blob_encrypted = 1) | ||||
| 			WHERE fetch_status = ? | ||||
| 			AND resource_id IN (SELECT id FROM resources WHERE encryption_blob_encrypted = 1) | ||||
| 			${excludedSql} | ||||
| 		`, [Resource.FETCH_STATUS_DONE]); | ||||
|  | ||||
| 		return r ? r.total : 0; | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const BaseItem = require('lib/models/BaseItem'); | ||||
| const BaseModel = require('lib/BaseModel'); | ||||
| const MasterKey = require('lib/models/MasterKey'); | ||||
| const Resource = require('lib/models/Resource'); | ||||
| const ResourceService = require('lib/services/ResourceService'); | ||||
| @@ -164,7 +165,7 @@ class DecryptionWorker { | ||||
| 					try { | ||||
| 						const decryptCounter = await this.kvStore().incValue(counterKey); | ||||
| 						if (decryptCounter > this.maxDecryptionAttempts_) { | ||||
| 							this.logger().debug(`DecryptionWorker: ${item.id} decryption has failed more than 2 times - skipping it`); | ||||
| 							this.logger().debug(`DecryptionWorker: ${BaseModel.modelTypeToName(item.type_)} ${item.id}: Decryption has failed more than 2 times - skipping it`); | ||||
| 							this.dispatch({ type: 'ENCRYPTION_HAS_DISABLED_ITEMS', value: true }); | ||||
| 							excludedIds.push(item.id); | ||||
| 							continue; | ||||
| @@ -231,7 +232,7 @@ class DecryptionWorker { | ||||
|  | ||||
| 		this.logger().info('DecryptionWorker: completed decryption.'); | ||||
|  | ||||
| 		const downloadedButEncryptedBlobCount = await Resource.downloadedButEncryptedBlobCount(); | ||||
| 		const downloadedButEncryptedBlobCount = await Resource.downloadedButEncryptedBlobCount(excludedIds); | ||||
|  | ||||
| 		this.state_ = 'idle'; | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,6 @@ class Synchronizer { | ||||
| 		this.logger_ = new Logger(); | ||||
| 		this.appType_ = appType; | ||||
| 		this.cancelling_ = false; | ||||
| 		this.autoStartDecryptionWorker_ = true; | ||||
| 		this.maxResourceSize_ = null; | ||||
| 		this.downloadQueue_ = null; | ||||
| 		this.clientId_ = Setting.value('clientId'); | ||||
| @@ -266,6 +265,10 @@ class Synchronizer { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	isFullSync(steps) { | ||||
| 		return steps.includes('update_remote') && steps.includes('delete_remote') && steps.includes('delta'); | ||||
| 	} | ||||
|  | ||||
| 	// Synchronisation is done in three major steps: | ||||
| 	// | ||||
| 	// 1. UPLOAD: Send to the sync target the items that have changed since the last sync. | ||||
| @@ -842,7 +845,7 @@ class Synchronizer { | ||||
| 		this.onProgress_ = function() {}; | ||||
| 		this.progressReport_ = {}; | ||||
|  | ||||
| 		this.dispatch({ type: 'SYNC_COMPLETED' }); | ||||
| 		this.dispatch({ type: 'SYNC_COMPLETED', isFullSync: this.isFullSync(syncSteps) }); | ||||
|  | ||||
| 		this.state_ = 'idle'; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user