|
@@ -0,0 +1,107 @@
|
|
|
+const log = require('../util/log')
|
|
|
+const path = require('path')
|
|
|
+const sqlite3 = require('sqlite3')
|
|
|
+const bplist = require('bplist-parser')
|
|
|
+const fs = require('fs')
|
|
|
+const plist = require('plist')
|
|
|
+
|
|
|
+// Derive filenames based on domain + file path
|
|
|
+const fileHash = require('../util/backup_filehash')
|
|
|
+
|
|
|
+const domain = 'AppDomainGroup-group.com.facebook.Messenger'
|
|
|
+
|
|
|
+module.exports.name = 'facebook_messenger_friends'
|
|
|
+module.exports.description = 'Show Facebook Messenger friends'
|
|
|
+
|
|
|
+// Specify this only works for iOS 9+
|
|
|
+module.exports.supportedVersions = '>=10.0'
|
|
|
+
|
|
|
+// Specify this reporter requires a backup.
|
|
|
+// The second parameter to func() is now a backup instead of the path to one.
|
|
|
+module.exports.requiresBackup = true
|
|
|
+
|
|
|
+// Specify this reporter supports the promises API for allowing chaining of reports.
|
|
|
+module.exports.usesPromises = true
|
|
|
+
|
|
|
+// You can also provide an array of functions instead of using `module.exports.func`.
|
|
|
+// These functions *should* be independent ranges to ensure reliable execution
|
|
|
+module.exports.functions = {
|
|
|
+
|
|
|
+ '>=10.0': function (program, backup, resolve, reject) {
|
|
|
+ // This function would be called for iOS 10+
|
|
|
+
|
|
|
+ backup.getFileManifest()
|
|
|
+ .then((items) => {
|
|
|
+ let filename = 'fbomnistore.db'
|
|
|
+ let fileitem = items.find((file) => {
|
|
|
+ if (file && file.relativePath)
|
|
|
+ return ~file.relativePath.indexOf(filename)
|
|
|
+ return false
|
|
|
+ })
|
|
|
+ if (fileitem) {
|
|
|
+ let filepath = fileitem.relativePath
|
|
|
+ let file = fileHash(filepath, domain)
|
|
|
+ return facebookMessengerFriendsReport(backup, file)
|
|
|
+ } else return [] // Return an empty array to the formatter, since no fbomnistore.db file can be found in the manifest
|
|
|
+ })
|
|
|
+ .then((items) => {
|
|
|
+ var result = program.formatter.format(items, {
|
|
|
+ program: program,
|
|
|
+ columns: {
|
|
|
+ 'Facebook Friend Usernames': el => el.field_value
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ resolve(result)
|
|
|
+ })
|
|
|
+ .catch((e) => {
|
|
|
+ console.log('[!] Encountered an Error:', e)
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ '>=5.0,<10.0': function (program, backup, resolve, reject) {
|
|
|
+ // This function would be called for all iOS 5 up to iOS 9.x.
|
|
|
+ // TODO
|
|
|
+ /*backup.getOldFileManifest()
|
|
|
+ .then((items) => {
|
|
|
+ var result = program.formatter.format(items, {
|
|
|
+ program: program,
|
|
|
+ columns: {
|
|
|
+ 'Facebook Friend Username': el => el.field_value
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ resolve(result)
|
|
|
+ })
|
|
|
+ .catch(reject)*/
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const facebookMessengerFriendsReport = (backup, file) => {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ var database = backup.getDatabase(file)
|
|
|
+ try {
|
|
|
+ database.get(`
|
|
|
+ SELECT name
|
|
|
+ FROM sqlite_master
|
|
|
+ WHERE type='table'
|
|
|
+ AND name LIKE 'collection_index#messenger_contacts_ios%'
|
|
|
+ LIMIT 1
|
|
|
+ `,
|
|
|
+ (err, table_name) => {
|
|
|
+ table_name = table_name.name
|
|
|
+ console.log("Table", table_name)
|
|
|
+ database.all(`
|
|
|
+ SELECT field_value
|
|
|
+ FROM '${table_name}'
|
|
|
+ WHERE field_name='username'
|
|
|
+ `, (err, rows) => {
|
|
|
+ resolve(rows)
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ reject(e)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|