All files decoder.js

100% Statements 67/67
100% Branches 28/28
100% Functions 4/4
100% Lines 59/59

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 901x 1x   1x 40x   10x   7x       22x   1x       1x 10x 10x 10x 10x 10x 10x 10x 10x   10x     1x 1x 1x 1x 1x   1x 1x 10x 6x 1x 1x   1x       7x 7x 7x 7x 7x 13x 13x   7x 7x     7x 1x   6x 1x 1x   5x   1x 1x 1x   4x 4x 4x   2x 2x 2x   2x 2x   7x 21x   7x    
const { decode } = require('64');
const BufferBuilder = require('buffer-builder');
 
module.exports = (encoding, body) => {
  switch (encoding) {
    case 'base64':
      return decode(clean(body));
    case 'quoted-printable':
      return convertQuotedPrintable(body);
    case '8bit':
    case '7bit':
    case 'binary':
      return body;
    default:
      throw new Error(`Unknown encoding ${encoding}body: ${body}`);
  }
};
 
const clean = (body) => {
  const buffBuild = new BufferBuilder();
  let start = 0;
  for (let i = 0; i < body.length; i++) {
    for (;i < body.length && body[i] !== CR && body[i] !== LF; i++);
    const end = i;
    for (;i < body.length && (body[i] === CR || body[i] === LF); i++);
    buffBuild.appendBuffer(body.slice(start, end));
    start = i;
  }
  return buffBuild.get();
};
 
const EQUALS = '='.charCodeAt(0);
const CR = '\r'.charCodeAt(0);
const LF = '\n'.charCodeAt(0);
const D = 'D'.charCodeAt(0);
const THREE = '3'.charCodeAt(0);
 
const translate = (() => {
  let str = 'switch (c) {\n';
  for (let i = 0; i <= 9; i++) str += `\tcase ${'0'.charCodeAt(0) + i}: return ${i};\n`;
  for (let i = 0xA; i <= 0xF; i++) str += `\tcase ${'A'.charCodeAt(0) + i - 0xA}: return ${i};\n`;
  str += 'default: return -1;\n';
  str += '};\n';
  /* eslint-disable no-new-func */
  return Function('c', str);
})();
 
function convertQuotedPrintable(body) {
  const len = body.length;
  const decoded = Buffer.alloc(len); // at most this big
  let j = 0;
  const runTo = len - 3;
  for (let i = 0; i < runTo; i++) {
    while (i < runTo && (decoded[j++] = body[i++]) !== EQUALS);
    if (i >= runTo) break;
    // We are dealing with a '=xx' sequence.
    const upper = body[i];
    const lower = body[++i];
 
    // fast path for =3D
    if (upper === THREE && lower === D) {
      continue;
    }
    if (upper === CR && lower === LF) {
      j--;
      continue;
    }
    if (upper === LF) {
      // windows chrome does invalid encoding with \n and not \r\n
      i--;
      j--;
      continue;
    }
    const upperTranslated = translate(upper);
    const lowerTranslated = translate(lower);
    if ((upperTranslated | lowerTranslated) & 128) {
      // invalid seq
      decoded[j++] = upper;
      decoded[j++] = lower;
      continue;
    }
    const shifted = upperTranslated << 4;
    decoded[j - 1] = shifted | lowerTranslated;
  }
  for (let i = runTo; i < len; i++) {
    decoded[j++] = body[i];
  }
  return decoded.slice(0, j);
}