mime-js

Create MIME message from browser, fork of https://github.com/ikr0m/mime-js
git clone git://git.defalsify.org/mime-js.git
Log | Files | Refs | LICENSE

mime-js.js (20649B)


      1 
      2 /*
      3     mime-js.js 0.2.0
      4     2014-10-18
      5 
      6     By Ikrom, https://github.com/ikr0m
      7     License: X11/MIT
      8  */
      9 
     10 (function() {
     11   window.Mime = (function() {
     12     var MailParser, _util, buildMimeObj, toMimeObj, toMimeTxt;
     13     toMimeTxt = function(mail, txtOnly) {
     14       var alternative, attaches, cids, createAlternative, createAttaches, createCids, createHtml, createMixed, createPlain, createRelated, getBoundary, htm, linkify, plain, related, result;
     15       linkify = function(inputText) {
     16         var replacePattern1, replacePattern2, replacePattern3, replacedText;
     17         replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
     18         replacedText = inputText.replace(replacePattern1, "<a href=\"$1\" target=\"_blank\">$1</a>");
     19         replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
     20         replacedText = replacedText.replace(replacePattern2, "$1<a href=\"http://$2\" target=\"_blank\">$2</a>");
     21         replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
     22         replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');
     23         return replacedText;
     24       };
     25       getBoundary = function() {
     26         var _random;
     27         _random = function() {
     28           return Math.random().toString(36).slice(2);
     29         };
     30         return _random() + _random();
     31       };
     32       createPlain = function(textContent) {
     33         if (textContent == null) {
     34           textContent = '';
     35         }
     36         return '\nContent-Type: text/plain; charset=UTF-8' + '\nContent-Transfer-Encoding: base64' + '\n\n' + (Base64.encode(textContent, true)).replace(/.{76}/g, "$&\n");
     37       };
     38       createHtml = function(msg) {
     39         var htmlContent;
     40         htmlContent = msg.body || "";
     41         htmlContent = htmlContent.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/, '&gt;').replace(/\n/g, '\n<br/>');
     42         htmlContent = linkify(htmlContent);
     43         htmlContent = '<div>' + htmlContent + '</div>';
     44         return '\nContent-Type: text/html; charset=UTF-8' + '\nContent-Transfer-Encoding: base64' + '\n\n' + (Base64.encode(htmlContent, true)).replace(/.{76}/g, "$&\n");
     45       };
     46       createAlternative = function(text, html) {
     47         var boundary;
     48         boundary = getBoundary();
     49         return '\nContent-Type: multipart/alternative; boundary=' + boundary + '\n\n--' + boundary + text + '\n\n--' + boundary + html + '\n\n--' + boundary + '--';
     50       };
     51       createCids = function(cids) {
     52         var base64, cid, cidArr, id, j, len, name, type;
     53         if (!cids) {
     54           return;
     55         }
     56         cidArr = [];
     57         for (j = 0, len = cids.length; j < len; j++) {
     58           cid = cids[j];
     59           type = cid.type;
     60           name = cid.name;
     61           base64 = cid.base64;
     62           id = getBoundary();
     63           cidArr.push('\nContent-Type: ' + type + '; name=\"' + name + '\"' + '\nContent-Transfer-Encoding: base64' + '\nContent-ID: <' + id + '>' + '\nX-Attachment-Id: ' + id + '\n\n' + base64);
     64         }
     65         return cidArr;
     66       };
     67       createRelated = function(alternative, cids) {
     68         var boundary, cid, j, len, relatedStr;
     69         if (cids == null) {
     70           cids = [];
     71         }
     72         boundary = getBoundary();
     73         relatedStr = '\nContent-Type: multipart/related; boundary=' + boundary + '\n\n--' + boundary + alternative;
     74         for (j = 0, len = cids.length; j < len; j++) {
     75           cid = cids[j];
     76           relatedStr += '\n--' + boundary + cid;
     77         }
     78         return relatedStr + '\n--' + boundary + '--';
     79       };
     80       createAttaches = function(attaches) {
     81         var attach, content, id, j, len, name, part, result, type;
     82         if (!attaches) {
     83           return;
     84         }
     85         result = [];
     86         for (j = 0, len = attaches.length; j < len; j++) {
     87           attach = attaches[j];
     88           type = attach.type;
     89           name = attach.name;
     90           id = getBoundary();
     91           content = '';
     92           part = '\nContent-Type: ' + type;
     93           if (name) {
     94             part += '; name=\"' + name + '\"' + '\nContent-Disposition: attachment; filename=\"' + name + '\"';
     95           }
     96           if (attach.base64) {
     97             content = attach.base64;
     98             part += '\nContent-Transfer-Encoding: base64';
     99           } else {
    100             content = attach.raw;
    101           }
    102           part += '\nX-Attachment-Id: ' + id;
    103           part += '\n\n' + content;
    104           result.push(part);
    105         }
    106         return result;
    107       };
    108       createMixed = function(related, attaches) {
    109         var attach, boundary, date, j, len, mailFromName, mimeStr, subject;
    110         boundary = getBoundary();
    111         subject = '';
    112         if (mail.subject) {
    113           subject = '=?UTF-8?B?' + Base64.encode(mail.subject, true) + '?=';
    114         }
    115         mailFromName = '=?UTF-8?B?' + Base64.encode(mail.fromName || "", true) + '?=';
    116         date = (new Date().toGMTString()).replace(/GMT|UTC/gi, '+0000');
    117         mimeStr = 'MIME-Version: 1.0' + '\nDate: ' + date + '\nMessage-ID: <' + getBoundary() + '@mail.your-domain.com>' + '\nSubject: ' + subject + '\nFrom: ' + mailFromName + ' <' + mail.from + '>' + (mail.to ? '\nTo: ' + mail.to : '') + (mail.cc ? '\nCc: ' + mail.cc : '') + '\nContent-Type: multipart/mixed; boundary=' + boundary + '\n\n--' + boundary + related;
    118         for (j = 0, len = attaches.length; j < len; j++) {
    119           attach = attaches[j];
    120           mimeStr += '\n--' + boundary + attach;
    121         }
    122         return (mimeStr + '\n--' + boundary + '--').replace(/\n/g, '\r\n');
    123       };
    124       plain = createPlain(mail.body);
    125       if (txtOnly) {
    126         related = plain;
    127       } else {
    128         htm = createHtml(mail);
    129         alternative = createAlternative(plain, htm);
    130         cids = createCids(mail.cids);
    131         related = createRelated(alternative, cids);
    132       }
    133       attaches = createAttaches(mail.attaches);
    134       result = createMixed(related, attaches);
    135       return result;
    136     };
    137     MailParser = function(rawMessage) {
    138       var cc, explodeMessage, from, getValidStr, messageParts, rawHeaders, subject, to;
    139       explodeMessage = function(inMessage) {
    140         var escBoundary, i, inBody, inBodyParts, inBoundary, inContentType, inContentTypeParts, inHeaderPos, inRawBody, inRawHeaders, match, mimeType, mimeTypeParts, regContentType, regString, specialChars;
    141         inHeaderPos = inMessage.indexOf("\r\n\r\n");
    142         if (inHeaderPos === -1) {
    143           inMessage = inMessage.replace(/\n/g, "\r\n");
    144           inHeaderPos = inMessage.indexOf("\r\n\r\n");
    145           if (inHeaderPos === -1) {
    146             inHeaderPos = inMessage.length;
    147           }
    148         }
    149         inRawHeaders = inMessage.slice(0, inHeaderPos).replace(/\r\n\s+/g, " ") + "\r\n";
    150         inRawBody = inMessage.slice(inHeaderPos).replace(/(\r\n)+$/, "").replace(/^(\r\n)+/, "");
    151         inContentType = "";
    152         regContentType = inRawHeaders.match(/Content-Type: (.*)/i);
    153         if (regContentType && regContentType.length > 0) {
    154           inContentType = regContentType[1];
    155         } else {
    156           console.log("Warning: MailParser: Content-type doesn't exist!");
    157         }
    158         inContentTypeParts = inContentType.split(";");
    159         mimeType = inContentTypeParts[0].replace(/\s/g, "");
    160         mimeTypeParts = mimeType.split("/");
    161         if (mimeTypeParts[0].toLowerCase() === "multipart") {
    162           inBodyParts = [];
    163           match = inContentTypeParts[1].match(/boundary="?([^"]*)"?/i);
    164           if (!match && inContentTypeParts[2]) {
    165             match = inContentTypeParts[2].match(/boundary="?([^"]*)"?/i);
    166           }
    167           inBoundary = _util.trim(match[1]).replace(/"/g, "");
    168           escBoundary = inBoundary.replace(/\+/g, "\\+");
    169           regString = new RegExp("--" + escBoundary, "g");
    170           inBodyParts = inRawBody.replace(regString, inBoundary).replace(regString, inBoundary).split(inBoundary);
    171           inBodyParts.shift();
    172           inBodyParts.pop();
    173           i = 0;
    174           while (i < inBodyParts.length) {
    175             inBodyParts[i] = inBodyParts[i].replace(/(\r\n)+$/, "").replace(/^(\r\n)+/, "");
    176             inBodyParts[i] = explodeMessage(inBodyParts[i]);
    177             i++;
    178           }
    179         } else {
    180           inBody = inRawBody;
    181           if (mimeTypeParts[0] === "text") {
    182             inBody = inBody.replace(RegExp("=\\r\\n", "g"), "");
    183             specialChars = inBody.match(RegExp("=[A-F0-9][A-F0-9]", "g"));
    184             if (specialChars) {
    185               i = 0;
    186               while (i < specialChars.length) {
    187                 inBody = inBody.replace(specialChars[i], String.fromCharCode(parseInt(specialChars[i].replace(RegExp("="), ""), 16)));
    188                 i++;
    189               }
    190             }
    191           }
    192         }
    193         return {
    194           rawHeaders: inRawHeaders,
    195           rawBody: inRawBody,
    196           body: inBody,
    197           contentType: inContentType,
    198           contentTypeParts: inContentTypeParts,
    199           boundary: inBoundary,
    200           bodyParts: inBodyParts,
    201           mimeType: mimeType,
    202           mimeTypeParts: mimeTypeParts
    203         };
    204       };
    205       messageParts = "";
    206       try {
    207         messageParts = explodeMessage(rawMessage);
    208       } catch (error) {}
    209       rawHeaders = messageParts.rawHeaders;
    210       getValidStr = function(arr) {
    211         if (arr == null) {
    212           arr = [];
    213         }
    214         return arr[1] || "";
    215       };
    216       subject = getValidStr(/\r\nSubject: (.*)\r\n/g.exec(rawHeaders));
    217       to = getValidStr(/\r\nTo: (.*)\r\n/g.exec(rawHeaders));
    218       cc = getValidStr(/\r\nCc: (.*)\r\n/g.exec(rawHeaders));
    219       from = getValidStr(/\r\nFrom: (.*)\r\n/g.exec(rawHeaders));
    220       return {
    221         messageParts: messageParts,
    222         subject: subject,
    223         to: to,
    224         cc: cc,
    225         from: from
    226       };
    227     };
    228     _util = (function() {
    229       var KOIRDec, QPDec, _decodeMimeWord, decode, decodeMimeWords, toHtmlEntity, trim, win1251Dec;
    230       trim = function(str) {
    231         if (str == null) {
    232           str = '';
    233         }
    234         return (typeof str.trim === "function" ? str.trim() : void 0) || str.replace(/^\s+|\s+$/g, '');
    235       };
    236       decode = function(txt, charset) {
    237         var result;
    238         if (txt == null) {
    239           txt = '';
    240         }
    241         if (charset == null) {
    242           charset = '';
    243         }
    244         charset = charset.toLowerCase();
    245         result = (function() {
    246           switch (false) {
    247             case charset.indexOf('koi8-r') === -1:
    248               return KOIRDec(txt);
    249             case charset.indexOf('utf-8') === -1:
    250               return Base64._utf8_decode(txt);
    251             case charset.indexOf('windows-1251') === -1:
    252               return win1251Dec(txt);
    253             default:
    254               return txt;
    255           }
    256         })();
    257         return result;
    258       };
    259       QPDec = function(s) {
    260         return s.replace(/\=[\r\n]+/g, "").replace(/\=[0-9A-F]{2}/gi, function(v) {
    261           return String.fromCharCode(parseInt(v.substr(1), 16));
    262         });
    263       };
    264       KOIRDec = function(str) {
    265         var charmap, code2char, i, j, len, res, val;
    266         charmap = unescape("%u2500%u2502%u250C%u2510%u2514%u2518%u251C%u2524%u252C%u2534%u253C%u2580%u2584%u2588%u258C%u2590" + "%u2591%u2592%u2593%u2320%u25A0%u2219%u221A%u2248%u2264%u2265%u00A0%u2321%u00B0%u00B2%u00B7%u00F7" + "%u2550%u2551%u2552%u0451%u2553%u2554%u2555%u2556%u2557%u2558%u2559%u255A%u255B%u255C%u255D%u255E" + "%u255F%u2560%u2561%u0401%u2562%u2563%u2564%u2565%u2566%u2567%u2568%u2569%u256A%u256B%u256C%u00A9" + "%u044E%u0430%u0431%u0446%u0434%u0435%u0444%u0433%u0445%u0438%u0439%u043A%u043B%u043C%u043D%u043E" + "%u043F%u044F%u0440%u0441%u0442%u0443%u0436%u0432%u044C%u044B%u0437%u0448%u044D%u0449%u0447%u044A" + "%u042E%u0410%u0411%u0426%u0414%u0415%u0424%u0413%u0425%u0418%u0419%u041A%u041B%u041C%u041D%u041E" + "%u041F%u042F%u0420%u0421%u0422%u0423%u0416%u0412%u042C%u042B%u0417%u0428%u042D%u0429%u0427%u042A");
    267         code2char = function(code) {
    268           if (code >= 0x80 && code <= 0xFF) {
    269             return charmap.charAt(code - 0x80);
    270           }
    271           return String.fromCharCode(code);
    272         };
    273         res = "";
    274         for (i = j = 0, len = str.length; j < len; i = ++j) {
    275           val = str[i];
    276           res = res + code2char(str.charCodeAt(i));
    277         }
    278         return res;
    279       };
    280       win1251Dec = function(str) {
    281         var i, iCode, j, len, oCode, result, s;
    282         if (str == null) {
    283           str = '';
    284         }
    285         result = '';
    286         for (i = j = 0, len = str.length; j < len; i = ++j) {
    287           s = str[i];
    288           iCode = str.charCodeAt(i);
    289           oCode = (function() {
    290             switch (false) {
    291               case iCode !== 168:
    292                 return 1025;
    293               case iCode !== 184:
    294                 return 1105;
    295               case !((191 < iCode && iCode < 256)):
    296                 return iCode + 848;
    297               default:
    298                 return iCode;
    299             }
    300           })();
    301           result = result + String.fromCharCode(oCode);
    302         }
    303         return result;
    304       };
    305       _decodeMimeWord = function(str, toCharset) {
    306         var encoding, fromCharset, match;
    307         str = _util.trim(str);
    308         fromCharset = void 0;
    309         encoding = void 0;
    310         match = void 0;
    311         match = str.match(/^\=\?([\w_\-]+)\?([QqBb])\?([^\?]*)\?\=$/i);
    312         if (!match) {
    313           return decode(str, toCharset);
    314         }
    315         fromCharset = match[1];
    316         encoding = (match[2] || "Q").toString().toUpperCase();
    317         str = (match[3] || "").replace(/_/g, " ");
    318         if (encoding === "B") {
    319           return Base64.decode(str, toCharset);
    320         } else if (encoding === "Q") {
    321           return QPDec(str);
    322         } else {
    323           return str;
    324         }
    325       };
    326       decodeMimeWords = function(str, toCharset) {
    327         str = (str || "").toString().replace(/(=\?[^?]+\?[QqBb]\?[^?]+\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]*\?=)/g, "$1").replace(/\=\?([\w_\-]+)\?([QqBb])\?[^\?]*\?\=/g, (function(mimeWord, charset, encoding) {
    328           return _decodeMimeWord(mimeWord);
    329         }).bind(this));
    330         return decode(str, toCharset);
    331       };
    332       toHtmlEntity = function(txt) {
    333         if (txt == null) {
    334           txt = "";
    335         }
    336         return (txt + "").replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    337       };
    338       return {
    339         decode: decode,
    340         KOIRDec: KOIRDec,
    341         win1251Dec: win1251Dec,
    342         decodeMimeWords: decodeMimeWords,
    343         toHtmlEntity: toHtmlEntity,
    344         trim: trim
    345       };
    346     })();
    347     buildMimeObj = function(rawMailObj) {
    348       var body, decodeBody, err, isHtml, isText, mergeInnerMsgs, mimeType, parseBodyParts, parts, readyMail, result, wrapPreTag;
    349       readyMail = {
    350         html: "",
    351         text: "",
    352         attaches: [],
    353         innerMsgs: [],
    354         to: _util.decodeMimeWords(rawMailObj.to),
    355         cc: _util.decodeMimeWords(rawMailObj.cc),
    356         from: _util.decodeMimeWords(rawMailObj.from),
    357         subject: _util.decodeMimeWords(rawMailObj.subject)
    358       };
    359       decodeBody = function(body, rawHeaders) {
    360         var decBody, isBase64, isQP;
    361         isQP = /Content-Transfer-Encoding: quoted-printable/i.test(rawHeaders);
    362         isBase64 = /Content-Transfer-Encoding: base64/i.test(rawHeaders);
    363         if (isBase64) {
    364           body = body.replace(/\s/g, '');
    365           decBody = typeof atob === "function" ? atob(body) : void 0;
    366           if (decBody == null) {
    367             decBody = Base64.decode(body);
    368           }
    369           body = decBody;
    370         } else if (isQP) {
    371           body = _util.QPDec(body);
    372         }
    373         return body;
    374       };
    375       parseBodyParts = function(bodyParts) {
    376         var attach, body, innerMsg, isAttach, isAudio, isHtml, isImg, isPlain, isQP, j, k, len, len1, mimeType, name, newMimeMsg, part, rawHeaders, ref, ref1, ref2, regex, slashPos, type, typePart;
    377         if (!bodyParts) {
    378           return;
    379         }
    380         for (j = 0, len = bodyParts.length; j < len; j++) {
    381           part = bodyParts[j];
    382           mimeType = ((ref = part.mimeType) != null ? ref : "").toLowerCase();
    383           if (mimeType.indexOf('multipart') !== -1) {
    384             parseBodyParts(part.bodyParts);
    385             continue;
    386           }
    387           if (mimeType.indexOf('message/rfc822') !== -1) {
    388             newMimeMsg = MailParser(part.rawBody);
    389             innerMsg = toMimeObj(newMimeMsg);
    390             readyMail.innerMsgs.push(innerMsg);
    391             continue;
    392           }
    393           rawHeaders = part.rawHeaders;
    394           isAttach = rawHeaders.indexOf('Content-Disposition: attachment') !== -1;
    395           body = part.rawBody;
    396           isHtml = /text\/html/.test(mimeType);
    397           isPlain = /text\/plain/.test(mimeType);
    398           isImg = /image/.test(mimeType);
    399           isAudio = /audio/.test(mimeType);
    400           if (isAttach || isImg || isAudio) {
    401             isQP = /Content-Transfer-Encoding: quoted-printable/i.test(rawHeaders);
    402             if (isQP) {
    403               body = _util.QPDec(body);
    404               body = btoa ? btoa(body) : Base64.encode(body);
    405             }
    406             ref1 = part.contentTypeParts;
    407             for (k = 0, len1 = ref1.length; k < len1; k++) {
    408               typePart = ref1[k];
    409               if (/name=/i.test(typePart)) {
    410                 name = typePart.replace(/(.*)=/, '').replace(/"|'/g, '');
    411                 break;
    412               }
    413             }
    414             if (!name) {
    415               name = isImg ? "image" : isAudio ? "audio" : "attachment";
    416               name += "_" + Math.floor(Math.random() * 100);
    417               slashPos = mimeType.indexOf('/');
    418               type = mimeType.substring(slashPos + 1);
    419               if (type.length < 4) {
    420                 name += "." + type;
    421               }
    422             }
    423             regex = /(.*)content-id:(.*)<(.*)>/i;
    424             attach = {
    425               type: mimeType,
    426               base64: body,
    427               name: name,
    428               cid: (ref2 = regex.exec(rawHeaders)) != null ? ref2[3] : void 0,
    429               visible: /png|jpeg|jpg|gif/.test(mimeType)
    430             };
    431             readyMail.attaches.push(attach);
    432           } else if (isHtml || isPlain) {
    433             body = decodeBody(body, rawHeaders);
    434             body = _util.decode(body, part.contentType);
    435             if (isHtml) {
    436               readyMail.html += body;
    437             }
    438             if (isPlain) {
    439               readyMail.text += body;
    440             }
    441           } else {
    442             console.log("Unknown mime type: " + mimeType);
    443           }
    444         }
    445         return null;
    446       };
    447       try {
    448         parts = rawMailObj.messageParts;
    449         if (!parts) {
    450           return readyMail;
    451         }
    452         mimeType = (parts.mimeType || "").toLowerCase();
    453         isText = /text\/plain/.test(mimeType);
    454         isHtml = /text\/html/.test(mimeType);
    455         if (mimeType.indexOf('multipart') !== -1) {
    456           parseBodyParts(parts.bodyParts);
    457         } else if (isText || isHtml) {
    458           body = decodeBody(parts.body, parts.rawHeaders);
    459           body = _util.decode(body, parts.contentType);
    460           if (isHtml) {
    461             readyMail.html = body;
    462           }
    463           if (isText) {
    464             readyMail.text = body;
    465           }
    466         } else {
    467           console.log("Warning: mime type isn't supported! mime=" + mimeType);
    468         }
    469       } catch (error) {
    470         err = error;
    471         throw new Error(err);
    472       }
    473       wrapPreTag = function(txt) {
    474         return "<pre>" + _util.toHtmlEntity(txt) + "</pre>";
    475       };
    476       mergeInnerMsgs = function(mail) {
    477         var htm, innerMsg, innerMsgs, j, len, msg, ref, txt;
    478         innerMsgs = mail.innerMsgs;
    479         if (innerMsgs != null ? innerMsgs.length : void 0) {
    480           if (!_util.trim(mail.html) && mail.text) {
    481             mail.html += wrapPreTag(mail.text);
    482           }
    483           for (j = 0, len = innerMsgs.length; j < len; j++) {
    484             innerMsg = innerMsgs[j];
    485             msg = mergeInnerMsgs(innerMsg);
    486             txt = msg.text;
    487             htm = msg.html;
    488             if (htm) {
    489               mail.html += htm;
    490             } else if (txt) {
    491               mail.html += wrapPerTag(txt);
    492               mail.text += txt;
    493             }
    494             if (((ref = msg.attaches) != null ? ref.length : void 0) > 0) {
    495               mail.attaches = mail.attaches.concat(msg.attaches);
    496             }
    497           }
    498         }
    499         return mail;
    500       };
    501       result = mergeInnerMsgs(readyMail);
    502       return result;
    503     };
    504     toMimeObj = function(mimeMsgText) {
    505       var mailObj, rawMailObj;
    506       rawMailObj = MailParser(mimeMsgText);
    507       mailObj = buildMimeObj(rawMailObj);
    508       return mailObj;
    509     };
    510     return {
    511       toMimeTxt: toMimeTxt,
    512       toMimeObj: toMimeObj
    513     };
    514   })();
    515 
    516 }).call(this);