const fileHash = require('../../util/backup_filehash') const log = require('../../util/log') const apple_timestamp = require('../../util/apple_timestamp') const NOTES_DB = fileHash('Library/Notes/notes.sqlite') const NOTES2_DB = fileHash('NoteStore.sqlite', 'AppDomainGroup-group.com.apple.notes') module.exports = { version: 4, name: 'notes', description: `List all iOS notes`, requiresBackup: true, // Run on a v3 lib / backup object. run (lib, { backup }) { return getAllNotes(backup) }, // Public facing properties output: { id: el => el.Z_PK, identifier: el => el.ZIDENTIFIER, modified: el => (el.XFORMATTEDDATESTRING || el.XFORMATTEDDATESTRING1) + '', passwordProtected: el => !!el.ZISPASSWORDPROTECTED, title: el => (el.ZTITLE || el.ZTITLE1 || el.ZTITLE2 || '').trim() || null, content: el => el.ZCONTENT || null } } function getAllNotes (backup) { return new Promise(async (resolve, reject) => { var newNotes // Try iOS 10/11 query. try { newNotes = await getNewNotesiOS10iOS11(backup) } catch (e) { log.verbose(`couldn't query notes as iOS10/11, trying iOS9`, e) } // If iOS 10/11 query fails, try iOS 9. if (newNotes == null) { try { newNotes = await getNewNotesiOS9(backup) } catch (e) { log.verbose(`couldn't query notes as iOS9`, e) } } // Try to fetch old notes database try { var oldNotes = await getOldNotes(backup) } catch (e) { log.verbose(`couldn't query old notes`, e) } // If we didn't get anything successfully, reject. if (newNotes == null && oldNotes == null) { return reject(new Error(`Couldn't find any known notes database in the system.`)) } // Join the notes together. let result = [...(newNotes || []), ...(oldNotes || [])] // console.log(result) log.verbose(result) resolve(result) }) } function getNewNotesiOS9 (backup) { return new Promise((resolve, reject) => { backup.openDatabase(NOTES2_DB) .then(db => { db.all(`SELECT ZICCLOUDSYNCINGOBJECT.*, ZICNOTEDATA.ZDATA as X_CONTENT_DATA, ${apple_timestamp.parse('ZCREATIONDATE')} AS XFORMATTEDDATESTRING FROM ZICCLOUDSYNCINGOBJECT LEFT JOIN ZICNOTEDATA ON ZICCLOUDSYNCINGOBJECT.ZNOTE = ZICNOTEDATA.ZNOTE`, async function (err, rows) { if (err) reject(err) resolve(rows) }) }) .catch(reject) }) } function getOldNotes (backup) { return new Promise((resolve, reject) => { backup.openDatabase(NOTES_DB) .then(db => { db.all(`SELECT *, ${apple_timestamp.parse('ZCREATIONDATE')} AS XFORMATTEDDATESTRING from ZNOTE LEFT JOIN ZNOTEBODY ON ZBODY = ZNOTEBODY.Z_PK`, function (err, rows) { if (err) reject(err) resolve(rows) }) }) .catch(reject) }) } function getNewNotesiOS10iOS11 (backup) { return new Promise((resolve, reject) => { backup.openDatabase(NOTES2_DB) .then(db => { db.all(`SELECT ZICCLOUDSYNCINGOBJECT.*, ZICNOTEDATA.ZDATA as X_CONTENT_DATA, ${apple_timestamp.parse('(ZCREATIONDATE')} AS XFORMATTEDDATESTRING, ${apple_timestamp.parse('ZCREATIONDATE1')} AS XFORMATTEDDATESTRING1 FROM ZICCLOUDSYNCINGOBJECT LEFT JOIN ZICNOTEDATA ON ZICCLOUDSYNCINGOBJECT.ZNOTE = ZICNOTEDATA.ZNOTE`, function (err, rows) { if (err) reject(err) resolve(rows) }) }) .catch(reject) }) }