Source: lib/buffer-utils.js

module.exports = BufferUtils

var crypto = require('crypto')
var constants = require('./constants')

/**
 * @namespace
 * @static
 */
function BufferUtils () {
  // static class
}

/**
 * @param {Buffer} data
 * @return {boolean}
 */
BufferUtils.isJPEG = function (data) {
  var h = BufferUtils._getHeader(data)

  return h && h.a === 0xFF && h.b === 0xD8 && h.c === 0xFF
}

/**
 * @param {Buffer} data
 * @return {boolean}
 */
BufferUtils.isPNG = function (data) {
  var h = BufferUtils._getHeader(data)

  return h && h.a === 0x89 && h.b === 0x50 && h.c === 0x4E && h.d === 0x47
}

/**
 * @param {Buffer} data
 * @return {boolean}
 */
BufferUtils.isImage = function (data) {
  return BufferUtils.isJPEG(data) || BufferUtils.isPNG(data)
}

/**
 * @param {Buffer} data
 * @return {boolean}
 */
BufferUtils.isMPEG4 = function (data) {
  var h = BufferUtils._getHeader(data, 4)

  // return h && h.a === 0x00 && h.b === 0x00 && h.c === 0x00
  return h && h.a === 0x66 && h.b === 0x74 && h.c === 0x79 && h.d === 0x70
}

/**
 * @param {Buffer} data
 * @return {boolean}
 */
BufferUtils.isMedia = function (data) {
  return BufferUtils.isImage(data) || BufferUtils.isMPEG4(data)
}

/**
 * @param {Buffer} data
 * @return {boolean}
 */
BufferUtils.isCompressed = function (data) {
  var h = BufferUtils._getHeader(data)

  // check for presence of a PK header
  return h && h.a === 0x50 && h.b === 0x4B
  // && h.c === 0x03 && h.d === 0x04
}

/**
 * @param {Buffer} data
 * @return {Object}
 */
BufferUtils._getHeader = function (data, index) {
  index = index || 0

  if (!data || data.length < index + 4) {
    return null
  } else {
    return {
      a: data.readUInt8(index + 0),
      b: data.readUInt8(index + 1),
      c: data.readUInt8(index + 2),
      d: data.readUInt8(index + 3)
    }
  }
}

/**
 * @param {Buffer} data
 * @return {string}
 */
BufferUtils.getFileExtension = function (data) {
  if (BufferUtils.isJPEG(data)) return '.jpg'
  if (BufferUtils.isPNG(data)) return '.png'
  if (BufferUtils.isMPEG4(data)) return '.mp4'
  if (BufferUtils.isCompressed(data)) return '.zip'
  return '.dat'
}

/**
 * @param {Buffer} data
 * @return {string}
 */
BufferUtils.sha256HashToBase64 = function (data) {
  return crypto.createHash('sha256').update(data).digest('base64')
}

/**
 * @param {Buffer} data
 * @return {string}
 */
BufferUtils.sha256HashToHex = function (data) {
  return crypto.createHash('sha256').update(data).digest('hex')
}

/**
 * @param {Buffer} a
 * @param {Buffer} b
 * @return {string}
 */
BufferUtils.hashSC = function (a, b) {
  var secret = new Buffer(constants.core.secret)

  var firstData = Buffer.concat([ secret, a ])
  var secondData = Buffer.concat([ b, secret ])

  var first = BufferUtils.sha256HashToHex(firstData)
  var second = BufferUtils.sha256HashToHex(secondData)

  var pattern = constants.core.hashPattern
  var hash = ''

  for (var i = 0; i < pattern.length; ++i) {
    if (pattern.charAt(i) === '0') {
      hash += first.charAt(i)
    } else {
      hash += second.charAt(i)
    }
  }

  return hash
}

/**
 * Decrypts Buffer data for stories.
 *
 * @param {Buffer} data
 * @param {string} key (base64)
 * @param {string} iv (base64)
 * @return {Buffer}
 */
BufferUtils.decryptStory = function (data, key, iv) {
  if (!data || !key || !iv) throw new Error('BufferUtils.decryptStory invalid params')

  var keyBuffer = new Buffer(key, 'base64')
  var ivBuffer = new Buffer(iv, 'base64')

  return BufferUtils.AES128DecryptedData(data, keyBuffer, ivBuffer)
}

/**
 * Decrypts Buffer data for stories.
 *
 * @param {Buffer} data
 * @param {Buffer} key
 * @param {Buffer} iv
 * @return {Buffer}
 */
BufferUtils.AES128DecryptedData = function (data, key, iv) {
  var keyLength = (key.length >= 32 ? 32 : 16)
  var algorithm = (keyLength === 32 ? 'aes-256-cbc' : 'aes-128-cbc')
  var decipher = crypto.createDecipheriv(algorithm, key.slice(0, keyLength), iv.slice(0, 16))

  return Buffer.concat([
    decipher.update(data),
    decipher.final()
  ])
}

/**
 * Generates a HTTP multipart/form-data boundary for the given key key,value pair.
 *
 * @param {string} key
 * @param {string} value
 * @return {Buffer}
 */
BufferUtils.boundaryForString = function (key, value) {
  return Buffer.concat([
    new Buffer('\r\n--' + constants.core.boundary + '\r\n'),
    new Buffer('Content-Disposition: form-data; name="' + key + '"\r\n\r\n'),
    new Buffer('' + value)
  ])
}

/**
 * Generates a HTTP multipart/form-data boundary for the given key key,value pair.
 *
 * @param {string} key
 * @param {Buffer} value
 * @return {Buffer}
 */
BufferUtils.boundaryForBuffer = function (key, value) {
  BufferUtils.assertBuffer(value)

  return Buffer.concat([
    new Buffer('\r\n--' + constants.core.boundary + '\r\n'),
    new Buffer('Content-Disposition: form-data; name="' + key + '"; filename="' + key + '"\r\n'),
    new Buffer('Content-Type: application/octet-stream\r\n\r\n'),
    value
  ])
}

BufferUtils.assertBuffer = function (data) {
  if (!Buffer.isBuffer(data)) {
    throw new Error('invalid Buffer', data)
  }
}