text: const fs = require('fs')
  const log = require('./log')
  
  // Format from here:
  // http://www.securitylearn.net/2012/10/27/cookies-binarycookies-reader/
  
  // January 1st, 2001, 00:00:00 UTC
  const APPLE_2001_EPOCH = 978307200
  
  function parseCookie (cookieBuff) {
    // Read a null-terminated string from the buffer.
    function readNullTerminatedString (fromIndex) {
      let string = ''
  
      for (var i = fromIndex; cookieBuff.readInt8(i) !== 0 && i < size; i++) {
        string += String.fromCharCode(cookieBuff.readInt8(i))
      }
  
      return string
    }
  
    let size = cookieBuff.readInt32LE(0)
    // 4 unknown bytes
    let flagInt = cookieBuff.readInt32LE(8)
    // 4 unknown bytes
    let urlOffset = cookieBuff.readInt32LE(16)
    let nameOffset = cookieBuff.readInt32LE(20)
    let pathOffset = cookieBuff.readInt32LE(24)
    let valueOffset = cookieBuff.readInt32LE(28)
    // END OF COOKIE 8 bytes = 0x0
    let expirationEpoch = cookieBuff.readDoubleLE(40) + APPLE_2001_EPOCH
    let creationEpoch = cookieBuff.readDoubleLE(48) + APPLE_2001_EPOCH
  
    // Dictionary of flag strings.
    let flagDict = {
      0: 'none',
      1: 'secure',
      4: 'httpOnly',
      5: 'secure,httpOnly'
    }
  
    let flags = flagDict[flagInt]
    let url = readNullTerminatedString(urlOffset)
    let name = readNullTerminatedString(nameOffset)
    let path = readNullTerminatedString(pathOffset)
    let value = readNullTerminatedString(valueOffset)
    let expiration = new Date(expirationEpoch * 1000)
    let creation = new Date(creationEpoch * 1000)
  
    return { url, name, value, path, flags, creation, expiration }
  }
  
  function parsePage (page) {
    function checkPageHeader (page) {
      return page.readInt32BE(0) === 0x00000100
    }
  
    // Header check fails page parse. return nothing.
    if (!checkPageHeader(page)) {
      return []
    }
  
    // Get the count of cookies on this page.
    const cookieCount = page.readInt32LE(4)
  
    // Store the cookies.
    let cookies = []
  
    for (let i = 0; i < cookieCount; i++) {
      // Read offset and size.
      const cookieOffset = page.readInt32LE(8 + i * 4)
      const cookieSize = page.readInt32LE(cookieOffset)
  
      // Slice buff
      const cookieBuff = page.slice(cookieOffset, cookieOffset + cookieSize)
  
      // Parse cookie
      let cookie = parseCookie(cookieBuff)
  
      // Add the cookie if parsing succeded.
      if (cookie) {
        cookies.push(cookie)
      }
    }
  
    return cookies
  }
  
  function parseBase (buff) {
    function checkHeader (buff) {
      return buff.readInt8(0) === 'c'.charCodeAt(0) &&
        buff.readInt8(1) === 'o'.charCodeAt(0) &&
        buff.readInt8(2) === 'o'.charCodeAt(0) &&
        buff.readInt8(3) === 'k'.charCodeAt(0)
    }
  
    // Header check fails. Return nothing.
    if (!checkHeader(buff)) {
      return []
    }
  
    let pageCount = buff.readInt32BE(4)
    let dataStart = (pageCount * 4) + 8
    let cursor = dataStart
  
    let cookies = []
  
    for (let i = 0; i < pageCount; i++) {
      // Find the page size, and grab the slice from the buffer.
      let pageSize = buff.readInt32BE(8 + i * 4)
      let page = buff.slice(cursor, cursor + pageSize)
  
      cookies = [...cookies, ...parsePage(page)]
  
      // Advance the cursor to the next page's tart index.
      cursor += pageSize
    }
  
    return cookies
  }
  
  // This parser works on best-effort, to allow for maximum data retrival.
  // If parsing fails, we return nothing, or as much as we can.
  // errors are only raised for out-of-bounds errors, etc.
  module.exports.parse = function (filePath) {
    return new Promise((resolve, reject) => {
  
      log.verbose('parse', filePath)
  
      try {
        let buff = fs.readFileSync(filePath)
        let result = parseBase(buff)
        // console.log(result)
        resolve(result)
      } catch (e) {
        return reject(e)
      }
    })
  }
  