calls_statistics.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. const fileHash = require('../../util/backup_filehash')
  2. const log = require('../../util/log')
  3. const CALLS_DB = '2b2b0084a1bc3a5ac8c27afdf14afb42c61a19ca'
  4. const CALLS2_DB = fileHash('Library/CallHistoryDB/CallHistory.storedata')
  5. module.exports = {
  6. version: 4,
  7. name: 'phone.calls_statistics',
  8. description: `Get statistics about all calls`,
  9. requiresBackup: true,
  10. // Run on a v3 lib / backup object.
  11. run (lib, { backup }) {
  12. return getCallsStatistics(backup)
  13. },
  14. // Manifest fields.
  15. // We need to find a value, so OR both of the data sources.
  16. output: {
  17. timerAll: el => el.timer_all || el.ZTIMER_ALL || 0,
  18. timerIncoming: el => el.timer_incoming || el.ZTIMER_INCOMING || 0,
  19. timerLast: el => el.timer_last || el.ZTIMER_LAST || 0,
  20. timerOutgoing: el => el.timer_outgoing || el.ZTIMER_OUTGOING || 0,
  21. timerLifetime: el => el.timer_lifetime || el.ZTIMER_LIFETIME || 0
  22. }
  23. }
  24. function getCallsStatistics (backup) {
  25. return new Promise(async (resolve, reject) => {
  26. try {
  27. var ios7stats = await getCallsStatisticsiOS7(backup)
  28. } catch (e) {
  29. log.verbose('tried ios7 stats', e)
  30. }
  31. try {
  32. var newerIOS = await getCallsStatisticsLater(backup)
  33. } catch (e) {
  34. log.verbose('tried ios7+ stats', e)
  35. }
  36. // Check if they both are not found.
  37. if (ios7stats == null && newerIOS == null) {
  38. return reject(new Error('no call stats found'))
  39. }
  40. // Resolve call logs.
  41. resolve({...(ios7stats || {}), ...(newerIOS || {})})
  42. })
  43. }
  44. function getCallsStatisticsiOS7 (backup) {
  45. /*
  46. This resolves to a data object, similar to this:
  47. {
  48. call_history_limit: 100,
  49. timer_last: 0,
  50. timer_outgoing: 0,
  51. timer_incoming: 0,
  52. timer_all: 0,
  53. timer_lifetime: 0,
  54. timer_last_reset: 0,
  55. kCallDBHasMigratedToCoreDataProperty: 0,
  56. _ClientVersion: 13,
  57. _UniqueIdentifier: '<uuid>'
  58. }
  59. */
  60. return new Promise((resolve, reject) => {
  61. backup.openDatabase(CALLS_DB)
  62. .then(db => {
  63. db.all(`SELECT * from _SqliteDatabaseProperties`, function (err, rows) {
  64. if (err) reject(err)
  65. var result = {}
  66. rows = rows || []
  67. for (var item of rows) {
  68. // Try to convert numbers to strings.
  69. if (/^[+-]?\d+$/.test(item.value)) {
  70. // Matches int regex
  71. result[item.key] = parseInt(item.value)
  72. } else if (/^[+-]?\d+\.\d+$/.test(item.value)) {
  73. // Matches float regex
  74. result[item.key] = parseFloat(item.value)
  75. } else {
  76. // Use existing value
  77. result[item.key] = item.value
  78. }
  79. }
  80. resolve(result)
  81. })
  82. })
  83. .catch(reject)
  84. })
  85. }
  86. function getCallsStatisticsLater (backup) {
  87. /*
  88. This resolves to a data object, similar to:
  89. {
  90. Z_PK: 1,
  91. Z_ENT: 1,
  92. Z_OPT: 14,
  93. ZTIMER_ALL: 0,
  94. ZTIMER_INCOMING: 2135,
  95. ZTIMER_LAST: 0,
  96. ZTIMER_LIFETIME: 6590,
  97. ZTIMER_OUTGOING: 4455
  98. }
  99. */
  100. return new Promise((resolve, reject) => {
  101. backup.openDatabase(CALLS2_DB)
  102. .then(db => {
  103. db.all(`SELECT * from ZCALLDBPROPERTIES`, function (err, rows) {
  104. if (err) reject(err)
  105. rows = rows || []
  106. resolve(rows[0])
  107. })
  108. })
  109. .catch(reject)
  110. })
  111. }