You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			169 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			169 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											7 years ago
										 | /* global window, Whisper, textsecure */ | ||
|  | 
 | ||
|  | const { isFunction } = require('lodash'); | ||
|  | 
 | ||
|  | const MessageDataMigrator = require('./messages_data_migrator'); | ||
|  | const { | ||
|  |   run, | ||
|  |   getLatestVersion, | ||
|  |   getDatabase, | ||
|  | } = require('./migrations/migrations'); | ||
|  | 
 | ||
|  | const MESSAGE_MINIMUM_VERSION = 7; | ||
|  | 
 | ||
|  | module.exports = { | ||
|  |   doesDatabaseExist, | ||
|  |   mandatoryMessageUpgrade, | ||
|  |   MESSAGE_MINIMUM_VERSION, | ||
|  |   migrateAllToSQLCipher, | ||
|  |   removeDatabase, | ||
|  |   runMigrations, | ||
|  | }; | ||
|  | 
 | ||
|  | async function runMigrations() { | ||
|  |   window.log.info('Run migrations on database with attachment data'); | ||
|  |   await run({ | ||
|  |     Backbone: window.Backbone, | ||
|  |     logger: window.log, | ||
|  |   }); | ||
|  | 
 | ||
|  |   Whisper.Database.migrations[0].version = getLatestVersion(); | ||
|  | } | ||
|  | 
 | ||
|  | async function mandatoryMessageUpgrade({ upgradeMessageSchema } = {}) { | ||
|  |   if (!isFunction(upgradeMessageSchema)) { | ||
|  |     throw new Error( | ||
|  |       'mandatoryMessageUpgrade: upgradeMessageSchema must be a function!' | ||
|  |     ); | ||
|  |   } | ||
|  | 
 | ||
|  |   const NUM_MESSAGES_PER_BATCH = 10; | ||
|  |   window.log.info( | ||
|  |     'upgradeMessages: Mandatory message schema upgrade started.', | ||
|  |     `Target version: ${MESSAGE_MINIMUM_VERSION}` | ||
|  |   ); | ||
|  | 
 | ||
|  |   let isMigrationWithoutIndexComplete = false; | ||
|  |   while (!isMigrationWithoutIndexComplete) { | ||
|  |     const database = getDatabase(); | ||
|  |     // eslint-disable-next-line no-await-in-loop
 | ||
|  |     const batchWithoutIndex = await MessageDataMigrator.processNextBatchWithoutIndex( | ||
|  |       { | ||
|  |         databaseName: database.name, | ||
|  |         minDatabaseVersion: database.version, | ||
|  |         numMessagesPerBatch: NUM_MESSAGES_PER_BATCH, | ||
|  |         upgradeMessageSchema, | ||
|  |         maxVersion: MESSAGE_MINIMUM_VERSION, | ||
|  |         BackboneMessage: Whisper.Message, | ||
|  |         saveMessage: window.Signal.Data.saveLegacyMessage, | ||
|  |       } | ||
|  |     ); | ||
|  |     window.log.info( | ||
|  |       'upgradeMessages: upgrade without index', | ||
|  |       batchWithoutIndex | ||
|  |     ); | ||
|  |     isMigrationWithoutIndexComplete = batchWithoutIndex.done; | ||
|  |   } | ||
|  |   window.log.info('upgradeMessages: upgrade without index complete!'); | ||
|  | 
 | ||
|  |   let isMigrationWithIndexComplete = false; | ||
|  |   while (!isMigrationWithIndexComplete) { | ||
|  |     // eslint-disable-next-line no-await-in-loop
 | ||
|  |     const batchWithIndex = await MessageDataMigrator.processNext({ | ||
|  |       BackboneMessage: Whisper.Message, | ||
|  |       BackboneMessageCollection: Whisper.MessageCollection, | ||
|  |       numMessagesPerBatch: NUM_MESSAGES_PER_BATCH, | ||
|  |       upgradeMessageSchema, | ||
|  |       getMessagesNeedingUpgrade: | ||
|  |         window.Signal.Data.getLegacyMessagesNeedingUpgrade, | ||
|  |       saveMessage: window.Signal.Data.saveLegacyMessage, | ||
|  |       maxVersion: MESSAGE_MINIMUM_VERSION, | ||
|  |     }); | ||
|  |     window.log.info('upgradeMessages: upgrade with index', batchWithIndex); | ||
|  |     isMigrationWithIndexComplete = batchWithIndex.done; | ||
|  |   } | ||
|  |   window.log.info('upgradeMessages: upgrade with index complete!'); | ||
|  | 
 | ||
|  |   window.log.info('upgradeMessages: Message schema upgrade complete'); | ||
|  | } | ||
|  | 
 | ||
|  | async function migrateAllToSQLCipher({ writeNewAttachmentData, Views } = {}) { | ||
|  |   if (!isFunction(writeNewAttachmentData)) { | ||
|  |     throw new Error( | ||
|  |       'migrateAllToSQLCipher: writeNewAttachmentData must be a function' | ||
|  |     ); | ||
|  |   } | ||
|  |   if (!Views) { | ||
|  |     throw new Error('migrateAllToSQLCipher: Views must be provided!'); | ||
|  |   } | ||
|  | 
 | ||
|  |   let totalMessages; | ||
|  |   const db = await Whisper.Database.open(); | ||
|  | 
 | ||
|  |   function showMigrationStatus(current) { | ||
|  |     const status = `${current}/${totalMessages}`; | ||
|  |     Views.Initialization.setMessage( | ||
|  |       window.i18n('migratingToSQLCipher', [status]) | ||
|  |     ); | ||
|  |   } | ||
|  | 
 | ||
|  |   try { | ||
|  |     totalMessages = await MessageDataMigrator.getNumMessages({ | ||
|  |       connection: db, | ||
|  |     }); | ||
|  |   } catch (error) { | ||
|  |     window.log.error( | ||
|  |       'background.getNumMessages error:', | ||
|  |       error && error.stack ? error.stack : error | ||
|  |     ); | ||
|  |     totalMessages = 0; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (totalMessages) { | ||
|  |     window.log.info(`About to migrate ${totalMessages} messages`); | ||
|  |     showMigrationStatus(0); | ||
|  |   } else { | ||
|  |     window.log.info('About to migrate non-messages'); | ||
|  |   } | ||
|  | 
 | ||
|  |   await window.Signal.migrateToSQL({ | ||
|  |     db, | ||
|  |     clearStores: Whisper.Database.clearStores, | ||
|  |     handleDOMException: Whisper.Database.handleDOMException, | ||
|  |     arrayBufferToString: textsecure.MessageReceiver.arrayBufferToStringBase64, | ||
|  |     countCallback: count => { | ||
|  |       window.log.info(`Migration: ${count} messages complete`); | ||
|  |       showMigrationStatus(count); | ||
|  |     }, | ||
|  |     writeNewAttachmentData, | ||
|  |   }); | ||
|  | 
 | ||
|  |   db.close(); | ||
|  | } | ||
|  | 
 | ||
|  | async function doesDatabaseExist() { | ||
|  |   return new Promise((resolve, reject) => { | ||
|  |     const { id } = Whisper.Database; | ||
|  |     const req = window.indexedDB.open(id); | ||
|  | 
 | ||
|  |     let existed = true; | ||
|  | 
 | ||
|  |     req.onerror = reject; | ||
|  |     req.onsuccess = () => { | ||
|  |       req.result.close(); | ||
|  |       resolve(existed); | ||
|  |     }; | ||
|  |     req.onupgradeneeded = () => { | ||
|  |       if (req.result.version === 1) { | ||
|  |         existed = false; | ||
|  |         window.indexedDB.deleteDatabase(id); | ||
|  |       } | ||
|  |     }; | ||
|  |   }); | ||
|  | } | ||
|  | 
 | ||
|  | function removeDatabase() { | ||
|  |   window.log.info(`Deleting IndexedDB database '${Whisper.Database.id}'`); | ||
|  |   window.indexedDB.deleteDatabase(Whisper.Database.id); | ||
|  | } |