postal-mime
Version:
Email parser for browser environments
97 lines (77 loc) • 2.7 kB
JavaScript
import { blobToArrayBuffer } from './decode-strings.js';
export default class QPDecoder {
constructor(opts) {
opts = opts || {};
this.decoder = opts.decoder || new TextDecoder();
this.maxChunkSize = 100 * 1024;
this.remainder = '';
this.chunks = [];
}
decodeQPBytes(encodedBytes) {
let buf = new ArrayBuffer(encodedBytes.length);
let dataView = new DataView(buf);
for (let i = 0, len = encodedBytes.length; i < len; i++) {
dataView.setUint8(i, parseInt(encodedBytes[i], 16));
}
return buf;
}
decodeChunks(str) {
// unwrap newlines
str = str.replace(/=\r?\n/g, '');
let list = str.split(/(?==)/);
let encodedBytes = [];
for (let part of list) {
if (part.charAt(0) !== '=') {
if (encodedBytes.length) {
this.chunks.push(this.decodeQPBytes(encodedBytes));
encodedBytes = [];
}
this.chunks.push(part);
continue;
}
if (part.length === 3) {
encodedBytes.push(part.substr(1));
continue;
}
if (part.length > 3) {
encodedBytes.push(part.substr(1, 2));
this.chunks.push(this.decodeQPBytes(encodedBytes));
encodedBytes = [];
part = part.substr(3);
this.chunks.push(part);
}
}
if (encodedBytes.length) {
this.chunks.push(this.decodeQPBytes(encodedBytes));
encodedBytes = [];
}
}
update(buffer) {
// expect full lines, so add line terminator as well
let str = this.decoder.decode(buffer) + '\n';
str = this.remainder + str;
if (str.length < this.maxChunkSize) {
this.remainder = str;
return;
}
this.remainder = '';
let partialEnding = str.match(/=[a-fA-F0-9]?$/);
if (partialEnding) {
if (partialEnding.index === 0) {
this.remainder = str;
return;
}
this.remainder = str.substr(partialEnding.index);
str = str.substr(0, partialEnding.index);
}
this.decodeChunks(str);
}
finalize() {
if (this.remainder.length) {
this.decodeChunks(this.remainder);
this.remainder = '';
}
// convert an array of arraybuffers into a blob and then back into a single arraybuffer
return blobToArrayBuffer(new Blob(this.chunks, { type: 'application/octet-stream' }));
}
}