mirror of
https://github.com/gchq/CyberChef.git
synced 2025-07-01 10:22:19 -04:00
Fixed the code just need to dress it up, update the comments, create the test cases
This commit is contained in:
parent
1d0d539797
commit
5f28349769
2 changed files with 184 additions and 257 deletions
|
@ -14,266 +14,128 @@ 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.
|
||||
* Both Base64 and QuotedPrintable is used for decode.
|
||||
*/
|
||||
class Mime {
|
||||
/**
|
||||
* Internet MessageFormat constructor
|
||||
* Mime 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.
|
||||
* Extract data from mimeObjects and return object array containing them.
|
||||
*
|
||||
* @param {string[][]} headerObjects
|
||||
* @param {boolean} header
|
||||
* @param {boolean} body
|
||||
* @param {boolean} recursive
|
||||
* @returns {object[]}
|
||||
*/
|
||||
toObjArray() {
|
||||
const out = [];
|
||||
Mime.walkMime(this.mimeObj, mimePart => out.push(mimePart));
|
||||
return out;
|
||||
extractData(headerObjects, header=true, body=true, recursive=true) {
|
||||
const output = [];
|
||||
Mime.walkMime(this.mimeObj, function(mimePart) {
|
||||
const outObj = {};
|
||||
outObj.fields = {};
|
||||
if (body) {
|
||||
const contType = Mime._extractField(mimePart, "content-type");
|
||||
if (contType && !contType.startsWith("multipart/")) {
|
||||
outObj.body = mimePart.body;
|
||||
} else {
|
||||
outObj.body = null;
|
||||
}
|
||||
}
|
||||
if (header) {
|
||||
outObj.header = mimePart.rawHeader;
|
||||
}
|
||||
if (!headerObjects) {
|
||||
output.push(outObj);
|
||||
return;
|
||||
}
|
||||
if (!Array.isArray(headerObjects)) {
|
||||
throw new OperationError("Invalid extraction in headers. Not an Array.");
|
||||
}
|
||||
headerObjects.forEach(function(obj) {
|
||||
if (!Array.isArray(obj)) {
|
||||
throw new OperationError("Invalid extraction in headers Object. Not an Array.");
|
||||
}
|
||||
switch (obj.length) {
|
||||
case 2:
|
||||
outObj.fields[obj[0]] = Mime._extractField(mimePart, obj[1]);
|
||||
break;
|
||||
case 3:
|
||||
outObj.fields[obj[0]] = Mime._extractField(mimePart, obj[1], obj[2]);
|
||||
break;
|
||||
default:
|
||||
throw new OperationError("Invalid extraction in headers. Invalid Array size.");
|
||||
}
|
||||
});
|
||||
output.push(outObj);
|
||||
}, recursive);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks a MIME document and returns an array of Mime data.
|
||||
* Common helper function to decode Mime encoded words in headers.
|
||||
*
|
||||
* @param {boolean} recursive
|
||||
*/
|
||||
decodeHeaderWords(recursive=true) {
|
||||
Mime.walkMime(this.mimeObj, mimePart => {
|
||||
if (mimePart.rawHeader) {
|
||||
mimePart.rawHeader = Mime.replaceEncodedWord(mimePart.rawHeader);
|
||||
}
|
||||
}, recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common helper function to decode Mime bodies.
|
||||
*
|
||||
* @param {boolean} recursive
|
||||
*/
|
||||
decodeMimeObjects(recursive=true) {
|
||||
Mime.walkMime(this.mimeObj,
|
||||
mimePart => Mime.decodeMimeMessage(mimePart),
|
||||
recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks a MIME document and returns a Mime Object.
|
||||
*
|
||||
* @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/")) {
|
||||
const mimeObj = Mime._splitParseHead(mimeData);
|
||||
const contType = Mime._extractField(mimeObj, "content-type");
|
||||
const boundary = Mime._extractField(mimeObj, "content-type", "boundary");
|
||||
if (mimeObj.body && 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
|
||||
return mimeObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes methods on a mime object. These methods should modify the mimeObj.
|
||||
* Executes a function on a mime object. These methods should modify the mimeObj.
|
||||
*
|
||||
* @param {Object} mimeObj
|
||||
* @param {function[]} methods
|
||||
* @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));
|
||||
static walkMime(mimeObj, method, recursive=true) {
|
||||
const contType = Mime._extractField(mimeObj, "content-type");
|
||||
method(mimeObj);
|
||||
if (recursive && mimeObj.body && contType && contType.startsWith("multipart/")) {
|
||||
mimeObj.body.forEach(obj => Mime.walkMime(obj, method));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,19 +143,18 @@ class Mime {
|
|||
* 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");
|
||||
const contType = Mime._extractField(mimeObj, "content-type");
|
||||
const contEnc = Mime._extractField(mimeObj, "content-transfer-encoding");
|
||||
let charEnc = Mime._extractField(mimeObj, "content-type", "charset");
|
||||
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);
|
||||
if (mimeObj.body && contEnc && typeof mimeObj.body === "string") {
|
||||
mimeObj.body = Mime._decodeMimeData(mimeObj.body, charEnc, contEnc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,10 +193,21 @@ class Mime {
|
|||
const matchObj = emlRegex.exec(input);
|
||||
if (matchObj) {
|
||||
const splitEmail = [input.substring(0, matchObj.index), input.substring(emlRegex.lastIndex)];
|
||||
return {rawHeader: splitEmail[0], body: splitEmail[1], header: Mime._parseHeader(splitEmail[0])};
|
||||
}
|
||||
return {rawHeader: input, body: null, header: Mime._parseHeader(input)};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static _parseHeader(input) {
|
||||
const sectionRegex = /([A-Za-z-]+):\s+([\x00-\xff]+?)(?=$|\r?\n\S)/g;
|
||||
const headerObj = {};
|
||||
let section;
|
||||
while ((section = sectionRegex.exec(splitEmail[0]))) {
|
||||
while ((section = sectionRegex.exec(input))) {
|
||||
const fieldName = section[1].toLowerCase();
|
||||
const fieldValue = Mime.replaceEncodedWord(section[2].replace(/\n|\r/g, " "));
|
||||
if (fieldName in headerObj) {
|
||||
|
@ -344,9 +216,7 @@ class Mime {
|
|||
headerObj[fieldName] = [fieldValue];
|
||||
}
|
||||
}
|
||||
return {rawHeader: splitEmail[0], body: splitEmail[1], header: headerObj};
|
||||
}
|
||||
return null;
|
||||
return headerObj;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,14 +243,14 @@ class Mime {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parses a complex header field and returns an object that contains
|
||||
* Parses a 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") {
|
||||
static _extractField(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++) {
|
||||
|
@ -389,7 +259,10 @@ class Mime {
|
|||
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];
|
||||
const val = (/(['"])(.+)\1/.exec(kv[1]));
|
||||
if (val && val.length === 3) {
|
||||
kv[1] = val[2];
|
||||
}
|
||||
}
|
||||
if (subfield.toLowerCase() === kv[0].toLowerCase()) {
|
||||
return kv[1];
|
||||
|
@ -397,7 +270,7 @@ class Mime {
|
|||
} else {
|
||||
throw OperationError("Not a valid header entry");
|
||||
}
|
||||
} else if (subfield == "value"){
|
||||
} else if (subfield === "value"){
|
||||
return fieldSplit[i].trim().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +278,6 @@ class Mime {
|
|||
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.
|
||||
|
@ -418,7 +290,7 @@ class Mime {
|
|||
const output = [];
|
||||
const newline = input.indexOf("\r") >= 0 ? "\r\n" : "\n";
|
||||
const boundaryStr = newline.concat("--", boundary);
|
||||
const last = input.indexOf(newline.concat("--", boundary, "--"));
|
||||
const last = input.indexOf(boundaryStr, "--");
|
||||
let begin = 0;
|
||||
for (let end = 0; end !== last; begin = end) {
|
||||
begin = input.indexOf(boundaryStr, begin);
|
||||
|
|
|
@ -9,7 +9,7 @@ import Mime from "../lib/Mime";
|
|||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Operation for parsing IMF messages into file list.
|
||||
*
|
||||
*/
|
||||
class ParseIMF extends Operation {
|
||||
|
||||
|
@ -18,12 +18,11 @@ class ParseIMF extends Operation {
|
|||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Parse Internet Message Format";
|
||||
this.module = "Default";
|
||||
this.description = ["Parse an IMF formatted messages following RFC5322.",
|
||||
"<br><br>",
|
||||
"Parses an IMF formated message like those sent in SMTP. These often have the file extention ".eml"e; and contain the email headers and body. The output will be a file list of the root header and decoded mime parts."
|
||||
"Parses an IMF formated message. These often have the file extention ".eml"e; and contain the email headers and body. The output will be a file list of the root header and decoded mime parts.",
|
||||
].join("\n");
|
||||
this.infoURL = "https://tools.ietf.org/html/rfc5322";
|
||||
this.inputType = "string";
|
||||
|
@ -31,7 +30,7 @@ class ParseIMF extends Operation {
|
|||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Decode Mime Encoded Words",
|
||||
"name": "Decode Encoded-Words",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
|
@ -39,12 +38,68 @@ class ParseIMF extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {string}
|
||||
* @param {Object[]}
|
||||
* 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[]}
|
||||
*/
|
||||
run(input, args) {
|
||||
return new Mime(input).decodeMime(args[0]);
|
||||
const eml = new Mime(input);
|
||||
if (!eml.mimeObj) {
|
||||
return [];
|
||||
}
|
||||
eml.decodeMimeObjects();
|
||||
if (args[0]) {
|
||||
eml.decodeHeaderWords(false);
|
||||
}
|
||||
const fields = [["filename", "content-disposition", "filename"],
|
||||
["name", "content-type", "name"],
|
||||
["type", "content-type"],
|
||||
["subject", "subject"]];
|
||||
const dataObj = eml.extractData(fields);
|
||||
let subject = null;
|
||||
const retval = [];
|
||||
if (dataObj.length >= 1) {
|
||||
subject = dataObj[0].fields.subject;
|
||||
if (dataObj[0].header) {
|
||||
retval.push(new File([dataObj[0].header], "Header.txt", {type: "text/plain"}));
|
||||
}
|
||||
}
|
||||
dataObj.forEach(function(obj) {
|
||||
if (obj.body) {
|
||||
let name = obj.fields.filename ? obj.fields.filename : obj.fields.name;
|
||||
const type = obj.fields.type ? obj.fields.type : "text/plain";
|
||||
if (!name) {
|
||||
name = (subject ? subject : "Undefined").concat(ParseIMF.getFileExt(type));
|
||||
}
|
||||
if (Array.isArray(obj.body)) {
|
||||
retval.push(new File([Uint8Array.from(obj.body)], name, {type: type}));
|
||||
} else {
|
||||
retval.push(new File([obj.body], name, {type: type}));
|
||||
}
|
||||
}
|
||||
});
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue