address_book.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. module.exports = {
  2. version: 4,
  3. name: 'phone.address_book',
  4. description: `List all address book records contained in the backup.`,
  5. requiresBackup: true,
  6. // Run on a v3 lib / backup object.
  7. run (lib, { backup }) {
  8. return getAddressBook(backup)
  9. },
  10. // Manifest fields.
  11. output: {
  12. id: el => el.ROWID,
  13. first: el => el.First || null,
  14. last: el => el.Last || null,
  15. organization: el => el.organization || null,
  16. phoneWork: el => el.phone_work || null,
  17. phoneMobile: el => el.phone_mobile || null,
  18. phoneHome: el => el.phone_home || null,
  19. email: el => el.email || null,
  20. createdDate: el => el.created_date || null,
  21. note: el => el.note || null,
  22. picture: el => !!el.profile_picture
  23. }
  24. }
  25. function getAddressBook (backup) {
  26. return new Promise((resolve, reject) => {
  27. backup.openDatabase(backup.getFileID('Library/AddressBook/AddressBook.sqlitedb'))
  28. .then(db => {
  29. // Query basic Address Book fields
  30. const query = `
  31. select ABPerson.ROWID
  32. , ABPerson.first
  33. , ABPerson.middle
  34. , ABPerson.last
  35. , ABPerson.Organization as organization
  36. , ABPerson.Department as department
  37. , ABPerson.Birthday as birthday
  38. , ABPerson.JobTitle as jobtitle
  39. , datetime(ABPerson.CreationDate + 978307200, 'unixepoch') as created_date
  40. , datetime(ABPerson.ModificationDate + 978307200, 'unixepoch') as updated_date
  41. , (select value from ABMultiValue where property = 3 and record_id = ABPerson.ROWID and label = (select ROWID from ABMultiValueLabel where value = '_$!<Work>!$_')) as phone_work
  42. , (select value from ABMultiValue where property = 3 and record_id = ABPerson.ROWID and label = (select ROWID from ABMultiValueLabel where value = '_$!<Mobile>!$_')) as phone_mobile
  43. , (select value from ABMultiValue where property = 3 and record_id = ABPerson.ROWID and label = (select ROWID from ABMultiValueLabel where value = '_$!<Home>!$_')) as phone_home
  44. , (select value from ABMultiValue where property = 4 and record_id = ABPerson.ROWID) as email
  45. , (select value from ABMultiValueEntry where parent_id in (select ROWID from ABMultiValue where record_id = ABPerson.ROWID) and key = (select ROWID from ABMultiValueEntryKey where lower(value) = 'street')) as address
  46. , (select value from ABMultiValueEntry where parent_id in (select ROWID from ABMultiValue where record_id = ABPerson.ROWID) and key = (select ROWID from ABMultiValueEntryKey where lower(value) = 'city')) as city
  47. , ABPerson.Note as note
  48. from ABPerson
  49. order by ABPerson.ROWID
  50. `
  51. db.all(query, async function (err, rows) {
  52. if (err) reject(err)
  53. const iterateElements = (elements, index, callback) => {
  54. if (index === elements.length) { return callback() }
  55. // do parse call with element
  56. let ele = elements[index]
  57. // Query username and profile links for other services (facebook etc)
  58. const query = `
  59. select (select value from ABMultiValue where property = 22 and record_id = ABPerson.ROWID and label = (select ROWID from ABMultiValueLabel where value = 'PROFILE')) as google_profile
  60. , (select value from ABMultiValue where property = 22 and record_id = ABPerson.ROWID and label = (select ROWID from ABMultiValueLabel where value = 'profile')) as google_profile1
  61. , (select value from ABMultiValue where property = 4 and record_id = ABPerson.ROWID and label = (select ROWID from ABMultiValueLabel where value = 'iCloud')) as icloud
  62. , (select value from ABMultiValueEntry where parent_id in (select ROWID from ABMultiValue where record_id = ABPerson.ROWID) and key = (select ROWID from ABMultiValueEntryKey where lower(value) = 'service')) as service
  63. , (select value from ABMultiValueEntry where parent_id in (select ROWID from ABMultiValue where record_id = ABPerson.ROWID) and key = (select ROWID from ABMultiValueEntryKey where lower(value) = 'username')) as username
  64. , (select value from ABMultiValueEntry where parent_id in (select ROWID from ABMultiValue where record_id = ABPerson.ROWID) and key = (select ROWID from ABMultiValueEntryKey where lower(value) = 'url')) as url
  65. from ABPerson
  66. where ABPerson.ROWID = ${ele.ROWID}
  67. order by ABPerson.ROWID
  68. `
  69. db.all(query, async function (err, rows1) {
  70. if (err) return reject(err)
  71. rows1[0].google_profile = rows1[0].google_profile || rows1[0].google_profile1
  72. delete rows1[0].google_profile1
  73. ele.services = rows1[0]
  74. backup.openDatabase(backup.getFileID('Library/AddressBook/AddressBookImages.sqlitedb'))
  75. .then(imageDB => {
  76. // Query profile picture extraction from /Library/AddressBook/AddressBookImages.sqlitedb
  77. const query = `
  78. select data
  79. from ABFullSizeImage
  80. where ABFullSizeImage.record_id = ${ele.ROWID}
  81. `
  82. imageDB.get(query, async function (err, row) {
  83. if (err) return reject(err)
  84. ele.profile_picture = null
  85. if (row) {
  86. ele.profile_picture = (row.data || '').toString('base64')
  87. }
  88. iterateElements(elements, index + 1, callback)
  89. })
  90. })
  91. .catch(reject)
  92. })
  93. }
  94. iterateElements(rows, 0, () => {
  95. resolve(rows)
  96. })
  97. })
  98. })
  99. .catch(reject)
  100. })
  101. }