440 lines
24 KiB
JavaScript
440 lines
24 KiB
JavaScript
/**
|
|
* @author bwhitn [brian.m.whitney@outlook.com]
|
|
* @copyright Crown Copyright 2016
|
|
* @license Apache-2.0
|
|
*/
|
|
|
|
import OperationError from "../errors/OperationError";
|
|
import cptable from "../vendor/js-codepage/cptable.js";
|
|
import {decodeQuotedPrintable} from "../lib/QuotedPrintable";
|
|
import {MIME_FORMAT} from "../lib/ChrEnc";
|
|
import Utils from "../Utils";
|
|
|
|
/**
|
|
* NOTE: Liberties taken include:
|
|
* No checks are made to verify quoted words are valid encodings e.g. underscore vs escape
|
|
* This attempts to decode mime reguardless if it is \r\n (correct newline) or \n (incorrect)
|
|
* Both Base64 and QuotedPrintable is used for decode. UUEncode is not available right now
|
|
* and is a standardized encoding format.
|
|
*/
|
|
class Mime {
|
|
/**
|
|
* Internet MessageFormat constructor
|
|
*/
|
|
constructor(input) {
|
|
this.mimeObj = Mime._parseMime(input);
|
|
}
|
|
|
|
/**
|
|
* Basic Email Parser that displays the header and mime sections as files.
|
|
* Args 0 boolean decode quoted words
|
|
*
|
|
* @param {string} input
|
|
* @param {boolean} decodeWords
|
|
* @returns {File[]}
|
|
*/
|
|
decodeMime(decodeWords) {
|
|
if (!this.mimeObj) {
|
|
return [];
|
|
}
|
|
//const emlObj = Mime._splitParseHead(this.input);
|
|
//if (!emlObj.body) {
|
|
// throw new OperationError("No body was found");
|
|
//}
|
|
if (decodeWords) {
|
|
emlObj.rawHeader = Mime.replaceEncodedWord(emlObj.rawHeader);
|
|
}
|
|
//const retval = [new File([emlObj.rawHeader], "Header", {type: "text/plain"})];
|
|
//let testval = Mime._parseMime(this.input);
|
|
testval.forEach(function(fileObj){
|
|
let name = fileObj.name;
|
|
if (fileObj.name === null) {
|
|
if (emlObj.header.hasOwnProperty("subject")) {
|
|
name = emlObj.header.subject[0];
|
|
} else {
|
|
name = "Undefined";
|
|
}
|
|
name = name.concat(Mime.getFileExt(fileObj.type));
|
|
}
|
|
retval.push(new File([Uint8Array.from(fileObj.data)], name, {type: fileObj.type}));
|
|
});
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
{
|
|
"rawHeader": "Message-ID: <39235FC5.276CCE00@example.com>\nDate: Wed, 17 May 2000 23:13:09 -0400\nFrom: Doug Sauder <dwsauder@example.com>\nX-Mailer: Mozilla 4.7 [en] (WinNT; I)\nX-Accept-Language: en\nMIME-Version: 1.0\nTo: Heinz =?iso-8859-1?Q?M=FCller?= <mueller@example.com>\nSubject: Die Hasen und die =?iso-8859-1?Q?Fr=F6sche?= (Netscape Messenger 4.7)\nContent-Type: multipart/mixed;\n boundary=\"------------A1E83A41894D3755390B838A\"",
|
|
"body": [
|
|
{
|
|
"rawHeader": "\nContent-Type: multipart/alternative;\n boundary=\"------------F03F94BA73D3B9E8C1B94D92\"",
|
|
"body": [
|
|
{
|
|
"rawHeader": "\nContent-Type: text/plain; charset=iso-8859-1\nContent-Transfer-Encoding: quoted-printable",
|
|
"body": "[blue ball]\n\nDie Hasen und die Fr=F6sche\n\nDie Hasen klagten einst =FCber ihre mi=DFliche Lage; \"wir leben\", sprach =\nein\nRedner, \"in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,\nder Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de=\nr\nTod selbst. Auf, la=DFt uns ein f=FCr allemal sterben.\"\n\nIn einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;\nallein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt\nerschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie=\n aufs\nschnellste untertauchten.\n\n\"Halt\", rief nun eben dieser Sprecher, \"wir wollen das Ers=E4ufen noch ei=\nn\nwenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,\nwelche also wohl noch ungl=FCcklicher sein m=FCssen als wir.\"\n\n[Image]\n\n\n",
|
|
"header": {
|
|
"content-type": [
|
|
"text/plain; charset=iso-8859-1"
|
|
],
|
|
"content-transfer-encoding": [
|
|
"quoted-printable"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"rawHeader": "\nContent-Type: multipart/related;\n boundary=\"------------C02FA3D0A04E95F295FB25EB\"",
|
|
"body": [
|
|
{
|
|
"rawHeader": "\nContent-Type: text/html; charset=us-ascii\nContent-Transfer-Encoding: 7bit",
|
|
"body": "<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">\n<html>\n<img SRC=\"cid:part1.39235FC5.E71D8178@example.com\" ALT=\"blue ball\" height=27 width=27><b></b>\n<p><b>Die Hasen und die Frösche</b>\n<p>Die Hasen klagten einst über ihre mißliche Lage; \"wir leben\",\nsprach ein Redner, \"in steter Furcht vor Menschen und Tieren, eine Beute\nder Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger\nals der Tod selbst. Auf, laßt uns ein für allemal sterben.\"\n<p>In einem nahen Teich wollten sie sich nun ersäufen; sie eilten\nihm zu; allein das außerordentliche Getöse und ihre wunderbare\nGestalt erschreckte eine Menge Frösche, die am Ufer saßen, so\nsehr, daß sie aufs schnellste untertauchten.\n<p>\"Halt\", rief nun eben dieser Sprecher, \"wir wollen das Ersäufen\nnoch ein wenig aufschieben, denn auch uns fürchten, wie ihr seht,\neinige Tiere, welche also wohl noch unglücklicher sein müssen\nals wir.\"\n<p><img SRC=\"cid:part2.39235FC5.E71D8178@example.com\" height=27 width=27>\n<br> \n<br> </html>\n",
|
|
"header": {
|
|
"content-type": [
|
|
"text/html; charset=us-ascii"
|
|
],
|
|
"content-transfer-encoding": [
|
|
"7bit"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"rawHeader": "\nContent-Type: image/png\nContent-ID: <part1.39235FC5.E71D8178@example.com>\nContent-Transfer-Encoding: base64\nContent-Disposition: inline; filename=\"C:\\TEMP\\nsmailEG.png\"",
|
|
"body": "iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA\nAAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ\nMZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY\nQsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K\ne+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB\nMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI\nSURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh\n5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW\nUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC\nUpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M\njRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C\nSYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom\nH3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N\nxW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi\neSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII=",
|
|
"header": {
|
|
"content-type": [
|
|
"image/png"
|
|
],
|
|
"content-id": [
|
|
"<part1.39235FC5.E71D8178@example.com>"
|
|
],
|
|
"content-transfer-encoding": [
|
|
"base64"
|
|
],
|
|
"content-disposition": [
|
|
"inline; filename=\"C:\\TEMP\\nsmailEG.png\""
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"rawHeader": "\nContent-Type: image/png\nContent-ID: <part2.39235FC5.E71D8178@example.com>\nContent-Transfer-Encoding: base64\nContent-Disposition: inline; filename=\"C:\\TEMP\\nsmail39.png\"",
|
|
"body": "iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV\nAAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH\nAAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb\nAAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa\nAAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl\nrKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr\npqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv\nV1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ\nAADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT\nAAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR\nAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+\nAAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA\nAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8\nLtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII\nSURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ\nScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja\n2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW\nSE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn\nOAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/\nuXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW\n0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw\nbNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8\nYVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms\n1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t\nZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg==",
|
|
"header": {
|
|
"content-type": [
|
|
"image/png"
|
|
],
|
|
"content-id": [
|
|
"<part2.39235FC5.E71D8178@example.com>"
|
|
],
|
|
"content-transfer-encoding": [
|
|
"base64"
|
|
],
|
|
"content-disposition": [
|
|
"inline; filename=\"C:\\TEMP\\nsmail39.png\""
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"header": {
|
|
"content-type": [
|
|
"multipart/related; boundary=\"------------C02FA3D0A04E95F295FB25EB\""
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"header": {
|
|
"content-type": [
|
|
"multipart/alternative; boundary=\"------------F03F94BA73D3B9E8C1B94D92\""
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"rawHeader": "\nContent-Type: image/png;\n name=\"redball.png\"\nContent-Transfer-Encoding: base64\nContent-Disposition: inline;\n filename=\"redball.png\"",
|
|
"body": "iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV\nAAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH\nAAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb\nAAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa\nAAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl\nrKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr\npqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv\nV1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ\nAADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT\nAAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR\nAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+\nAAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA\nAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8\nLtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII\nSURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ\nScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja\n2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW\nSE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn\nOAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/\nuXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW\n0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw\nbNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8\nYVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms\n1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t\nZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg==",
|
|
"header": {
|
|
"content-type": [
|
|
"image/png; name=\"redball.png\""
|
|
],
|
|
"content-transfer-encoding": [
|
|
"base64"
|
|
],
|
|
"content-disposition": [
|
|
"inline; filename=\"redball.png\""
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"rawHeader": "\nContent-Type: image/png;\n name=\"greenball.png\"\nContent-Transfer-Encoding: base64\nContent-Disposition: inline;\n filename=\"greenball.png\"",
|
|
"body": "iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA\nIQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh\nrQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC\nzhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY\n1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp\n1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt\nSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b\nVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ\naTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm\nkiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV\nG9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex\nI0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr\nYUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0\nRVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C\nYII=",
|
|
"header": {
|
|
"content-type": [
|
|
"image/png; name=\"greenball.png\""
|
|
],
|
|
"content-transfer-encoding": [
|
|
"base64"
|
|
],
|
|
"content-disposition": [
|
|
"inline; filename=\"greenball.png\""
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"header": {
|
|
"message-id": [
|
|
"<39235FC5.276CCE00@example.com>"
|
|
],
|
|
"date": [
|
|
"Wed, 17 May 2000 23:13:09 -0400"
|
|
],
|
|
"from": [
|
|
"Doug Sauder <dwsauder@example.com>"
|
|
],
|
|
"x-mailer": [
|
|
"Mozilla 4.7 [en] (WinNT; I)"
|
|
],
|
|
"x-accept-language": [
|
|
"en"
|
|
],
|
|
"mime-version": [
|
|
"1.0"
|
|
],
|
|
"to": [
|
|
"Heinz Müller <mueller@example.com>"
|
|
],
|
|
"subject": [
|
|
"Die Hasen und die Frösche (Netscape Messenger 4.7)"
|
|
],
|
|
"content-type": [
|
|
"multipart/mixed; boundary=\"------------A1E83A41894D3755390B838A\""
|
|
]
|
|
}
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* Simple function to add a common file extention based on mime type string.
|
|
*
|
|
* @param {string} mimetype
|
|
* @returns {string}
|
|
*/
|
|
static getFileExt(mimetype) {
|
|
switch (mimetype) {
|
|
case "text/plain":
|
|
return ".txt";
|
|
case "text/html":
|
|
return ".htm";
|
|
case "application/rtf":
|
|
return ".rtf";
|
|
}
|
|
return ".bin";
|
|
}
|
|
|
|
/**
|
|
* Helper function to return objects as an array.
|
|
*
|
|
* @returns {object[]}
|
|
*/
|
|
toObjArray() {
|
|
const out = [];
|
|
Mime.walkMime(this.mimeObj, mimePart => out.push(mimePart));
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Walks a MIME document and returns an array of Mime data.
|
|
*
|
|
* @param {string} mimeData
|
|
* @returns {object}
|
|
*/
|
|
static _parseMime(mimeData) {
|
|
let mimeObj = Mime._splitParseHead(mimeData);
|
|
const contType = Mime.decodeComplexField(mimeObj, "content-type");
|
|
const boundary = Mime.decodeComplexField(mimeObj, "content-type", "boundary");
|
|
if (contType && contType.startsWith("multipart/")) {
|
|
if (!boundary) {
|
|
throw new OperationError("Invalid mulitpart section no boundary");
|
|
}
|
|
const sections = [];
|
|
Mime._splitMultipart(mimeObj.body, boundary).forEach((mimePart) => {
|
|
sections.push(Mime._parseMime(mimePart));
|
|
}, sections);
|
|
mimeObj.body = sections;
|
|
}
|
|
return mimeObj
|
|
}
|
|
|
|
/**
|
|
* Executes methods on a mime object. These methods should modify the mimeObj.
|
|
*
|
|
* @param {Object} mimeObj
|
|
* @param {function[]} methods
|
|
* @param {boolean} recursive
|
|
* @returns {null}
|
|
*/
|
|
static walkMime(mimeObj, methods, recursive=true) {
|
|
let contType = Mime.decodeComplexField(mimeObj, "content-type");
|
|
if (recursive && contType && contType.startsWith("mulitpart/")) {
|
|
mimeObj.body.forEach(obj => Mime.walkMime(obj, methods));
|
|
} else {
|
|
methods.forEach(method => method(mimeObj));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to decode a mimeObj's data by applying appropriate character and content decoders based on the header data.
|
|
*
|
|
* @param {Object} mimeObj
|
|
* @returns {null}
|
|
*/
|
|
static decodeMimeMessage(mimeObj) {
|
|
let contType = Mime.decodeComplexField(mimeObj, "content-type"),
|
|
charEnc = Mime.decodeComplexField(mimeObj, "content-type", "charset"),
|
|
contEnc = Mime.decodeComplexField(mimeObj, "content-transfer-encoding");
|
|
if (contType != null) {
|
|
if (!charEnc && contType.startsWith("text/")) {
|
|
charEnc = "us-ascii";
|
|
}
|
|
}
|
|
if (contEnc && typeof mimeObj.body === "string") {
|
|
mimeObj.body = Mime.decodeMimeData(mimeObj.body, charEnc, contEnc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Takes a string and decodes quoted words inside them
|
|
* These take the form of:
|
|
* input "=?utf-8?Q?Hello_World!?="
|
|
* output "Hello World!"
|
|
*
|
|
* @param {string} input
|
|
* @param {string} type
|
|
* @returns {string}
|
|
*/
|
|
static replaceEncodedWord(input) {
|
|
return input.replace(/=\?([^?]+)\?(Q|B)\?([^?]+)\?=/g, function (a, charEnc, contEnc, input) {
|
|
contEnc = (contEnc === "B") ? "base64" : "quoted-printable";
|
|
if (contEnc === "quoted-printable") {
|
|
input = input.replace(/_/g, " ");
|
|
}
|
|
return Utils.byteArrayToUtf8(Mime._decodeMimeData(input, charEnc, contEnc));
|
|
});
|
|
}
|
|
|
|
|
|
//TODO: Allow only a header as input
|
|
/**
|
|
* Breaks the header from the body and parses the header. The returns an
|
|
* object or null. The object contains the raw header, decoded body, and
|
|
* parsed header object.
|
|
*
|
|
* @param {string} input
|
|
* @returns {object}
|
|
*/
|
|
static _splitParseHead(input) {
|
|
const emlRegex = /(?:\r?\n){2}/g;
|
|
const matchObj = emlRegex.exec(input);
|
|
if (matchObj) {
|
|
const splitEmail = [input.substring(0, matchObj.index), input.substring(emlRegex.lastIndex)];
|
|
const sectionRegex = /([A-Za-z-]+):\s+([\x00-\xff]+?)(?=$|\r?\n\S)/g;
|
|
const headerObj = {};
|
|
let section;
|
|
while ((section = sectionRegex.exec(splitEmail[0]))) {
|
|
const fieldName = section[1].toLowerCase();
|
|
const fieldValue = Mime.replaceEncodedWord(section[2].replace(/\n|\r/g, " "));
|
|
if (fieldName in headerObj) {
|
|
headerObj[fieldName].push(fieldValue);
|
|
} else {
|
|
headerObj[fieldName] = [fieldValue];
|
|
}
|
|
}
|
|
return {rawHeader: splitEmail[0], body: splitEmail[1], header: headerObj};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return decoded MIME data given the character encoding and content encoding.
|
|
*
|
|
* @param {string} input
|
|
* @param {string} charEnc
|
|
* @param {string} contEnc
|
|
* @returns {string}
|
|
*/
|
|
static _decodeMimeData(input, charEnc, contEnc) {
|
|
switch (contEnc) {
|
|
case "base64":
|
|
input = Utils.convertToByteArray(input, "base64");
|
|
break;
|
|
case "quoted-printable":
|
|
input = decodeQuotedPrintable(input);
|
|
}
|
|
if (charEnc && MIME_FORMAT.hasOwnProperty(charEnc.toLowerCase())) {
|
|
input = Utils.strToByteArray(cptable.utils.decode(MIME_FORMAT[charEnc.toLowerCase()], input));
|
|
|
|
}
|
|
return input;
|
|
}
|
|
|
|
/**
|
|
* Parses a complex header field and returns an object that contains
|
|
* normalized keys with corresponding values along with single values under
|
|
* a value array.
|
|
*
|
|
* @param {string} field
|
|
* @returns {string}
|
|
*/
|
|
static decodeComplexField(mimeObj, field, subfield="value") {
|
|
if (mimeObj.header.hasOwnProperty(field)) {
|
|
const fieldSplit = mimeObj.header[field][0].split(/;\s+/g);
|
|
for (let i = 0; i < fieldSplit.length; i++) {
|
|
const eq = fieldSplit[i].indexOf("=");
|
|
if (eq >= 0) {
|
|
if (fieldSplit[i].length > eq) {
|
|
const kv = [fieldSplit[i].substring(0, eq), fieldSplit[i].substring(eq + 1).trim()];
|
|
if ((kv[1].startsWith("'") && kv[1].endsWith("'")) || (kv[1].startsWith("\"") && kv[1].endsWith("\""))) {
|
|
kv[1] = (/(['"])(.+)\1/.exec(kv[1]))[2];
|
|
}
|
|
if (subfield.toLowerCase() === kv[0].toLowerCase()) {
|
|
return kv[1];
|
|
}
|
|
} else {
|
|
throw OperationError("Not a valid header entry");
|
|
}
|
|
} else if (subfield == "value"){
|
|
return fieldSplit[i].trim().toLowerCase();
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
//TODO: make this a yield instead of string array.
|
|
/**
|
|
* Splits a Mime document by the current boundaries and attempts to account
|
|
* for the current new line size which can be either the standard \r\n or \n.
|
|
*
|
|
* @param {string} input
|
|
* @param {string} boundary
|
|
* @return {string[]}
|
|
*/
|
|
static _splitMultipart(input, boundary) {
|
|
const output = [];
|
|
const newline = input.indexOf("\r") >= 0 ? "\r\n" : "\n";
|
|
const boundaryStr = newline.concat("--", boundary);
|
|
const last = input.indexOf(newline.concat("--", boundary, "--"));
|
|
let begin = 0;
|
|
for (let end = 0; end !== last; begin = end) {
|
|
begin = input.indexOf(boundaryStr, begin);
|
|
if (begin < 0) {
|
|
break;
|
|
}
|
|
begin += boundaryStr.length;
|
|
end = input.indexOf(boundaryStr, begin);
|
|
if (end <= begin) {
|
|
break;
|
|
}
|
|
output.push(input.substring(begin, end));
|
|
}
|
|
return output;
|
|
}
|
|
}
|
|
|
|
export default Mime;
|