207 lines
5.4 KiB
JavaScript
207 lines
5.4 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const libmime = require('libmime');
|
||
|
|
||
|
/**
|
||
|
* Class Headers to parse and handle message headers. Headers instance allows to
|
||
|
* check existing, delete or add new headers
|
||
|
*/
|
||
|
class Headers {
|
||
|
constructor(headers, config) {
|
||
|
config = config || {};
|
||
|
|
||
|
if (Array.isArray(headers)) {
|
||
|
// already using parsed headers
|
||
|
this.changed = true;
|
||
|
this.headers = false;
|
||
|
this.parsed = true;
|
||
|
this.lines = headers;
|
||
|
} else {
|
||
|
// using original string/buffer headers
|
||
|
this.changed = false;
|
||
|
this.headers = headers;
|
||
|
this.parsed = false;
|
||
|
this.lines = false;
|
||
|
}
|
||
|
this.mbox = false;
|
||
|
this.http = false;
|
||
|
|
||
|
this.libmime = new libmime.Libmime({ Iconv: config.Iconv });
|
||
|
}
|
||
|
|
||
|
get(key) {
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
key = this._normalizeHeader(key);
|
||
|
let lines = this.lines.filter(line => line.key === key).map(line => line.line);
|
||
|
|
||
|
return lines;
|
||
|
}
|
||
|
|
||
|
getDecoded(key) {
|
||
|
return this.get(key)
|
||
|
.map(line => this.libmime.decodeHeader(line))
|
||
|
.filter(line => line && line.value);
|
||
|
}
|
||
|
|
||
|
getFirst(key) {
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
key = this._normalizeHeader(key);
|
||
|
let header = this.lines.find(line => line.key === key);
|
||
|
if (!header) {
|
||
|
return '';
|
||
|
}
|
||
|
return ((this.libmime.decodeHeader(header.line) || {}).value || '').toString().trim();
|
||
|
}
|
||
|
|
||
|
getList() {
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
return this.lines;
|
||
|
}
|
||
|
|
||
|
add(key, value, index) {
|
||
|
if (typeof value === 'undefined') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (typeof value === 'number') {
|
||
|
value = value.toString();
|
||
|
}
|
||
|
|
||
|
if (typeof value === 'string') {
|
||
|
value = Buffer.from(value);
|
||
|
}
|
||
|
|
||
|
value = value.toString('binary');
|
||
|
this.addFormatted(key, this.libmime.foldLines(key + ': ' + value.replace(/\r?\n/g, ''), 76, false), index);
|
||
|
}
|
||
|
|
||
|
addFormatted(key, line, index) {
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
index = index || 0;
|
||
|
this.changed = true;
|
||
|
let header = {
|
||
|
key: this._normalizeHeader(key),
|
||
|
line
|
||
|
};
|
||
|
|
||
|
if (index < 1) {
|
||
|
this.lines.unshift(header);
|
||
|
} else if (index >= this.lines.length) {
|
||
|
this.lines.push(header);
|
||
|
} else {
|
||
|
this.lines.splice(index, 0, header);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
remove(key) {
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
key = this._normalizeHeader(key);
|
||
|
for (let i = this.lines.length - 1; i >= 0; i--) {
|
||
|
if (this.lines[i].key === key) {
|
||
|
this.changed = true;
|
||
|
this.lines.splice(i, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
update(key, value) {
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
let keyName = key;
|
||
|
let index = 0;
|
||
|
key = this._normalizeHeader(key);
|
||
|
for (let i = this.lines.length - 1; i >= 0; i--) {
|
||
|
if (this.lines[i].key === key) {
|
||
|
index = i;
|
||
|
this.changed = true;
|
||
|
this.lines.splice(i, 1);
|
||
|
}
|
||
|
}
|
||
|
this.add(keyName, value, index);
|
||
|
}
|
||
|
|
||
|
build(lineEnd) {
|
||
|
if (!this.changed && !lineEnd) {
|
||
|
return typeof this.headers === 'string' ? Buffer.from(this.headers, 'binary') : this.headers;
|
||
|
}
|
||
|
|
||
|
if (!this.parsed) {
|
||
|
this._parseHeaders();
|
||
|
}
|
||
|
|
||
|
lineEnd = lineEnd || '\r\n';
|
||
|
|
||
|
let headers = this.lines.map(line => line.line.replace(/\r?\n/g, lineEnd)).join(lineEnd) + `${lineEnd}${lineEnd}`;
|
||
|
|
||
|
if (this.mbox) {
|
||
|
headers = this.mbox + lineEnd + headers;
|
||
|
}
|
||
|
|
||
|
if (this.http) {
|
||
|
headers = this.http + lineEnd + headers;
|
||
|
}
|
||
|
|
||
|
return Buffer.from(headers, 'binary');
|
||
|
}
|
||
|
|
||
|
_normalizeHeader(key) {
|
||
|
return (key || '').toLowerCase().trim();
|
||
|
}
|
||
|
|
||
|
_parseHeaders() {
|
||
|
if (!this.headers) {
|
||
|
this.lines = [];
|
||
|
this.parsed = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let lines = this.headers
|
||
|
.toString('binary')
|
||
|
.replace(/[\r\n]+$/, '')
|
||
|
.split(/\r?\n/);
|
||
|
|
||
|
for (let i = lines.length - 1; i >= 0; i--) {
|
||
|
let chr = lines[i].charAt(0);
|
||
|
if (i && (chr === ' ' || chr === '\t')) {
|
||
|
lines[i - 1] += '\r\n' + lines[i];
|
||
|
lines.splice(i, 1);
|
||
|
} else {
|
||
|
let line = lines[i];
|
||
|
if (!i && /^From /i.test(line)) {
|
||
|
// mbox file
|
||
|
this.mbox = line;
|
||
|
lines.splice(i, 1);
|
||
|
continue;
|
||
|
} else if (!i && /^POST /i.test(line)) {
|
||
|
// HTTP POST request
|
||
|
this.http = line;
|
||
|
lines.splice(i, 1);
|
||
|
continue;
|
||
|
}
|
||
|
let key = this._normalizeHeader(line.substr(0, line.indexOf(':')));
|
||
|
lines[i] = {
|
||
|
key,
|
||
|
line
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.lines = lines;
|
||
|
this.parsed = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// expose to the world
|
||
|
module.exports = Headers;
|