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

commit ad9a5e48eed405b6b4f84b8371312b70ab32e04d
Author: Ikrom <ikrom@tocobox.com>
Date:   Sat, 18 Oct 2014 14:31:11 +0500

initial commit

Diffstat:
A.gitignore | 18++++++++++++++++++
ALICENSE.md | 9+++++++++
Aapp/assets/javascripts/mime-js.coffee | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/controllers/Application.scala | 13+++++++++++++
Aapp/views/index.scala.html | 31+++++++++++++++++++++++++++++++
Abuild.sbt | 8++++++++
Aconf/application.conf | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/routes | 12++++++++++++
Aproject/build.properties | 2++
Aproject/plugins.sbt | 6++++++
Apublic/images/favicon.png | 0
Apublic/javascripts/base64.js | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apublic/javascripts/mime-js.js | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apublic/stylesheets/main.css | 0
14 files changed, 512 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,17 @@ +logs +project/project +project/target +target +tmp +.history +dist +/.idea +/*.iml +/out +/.idea_modules +/.classpath +/.project +/RUNNING_PID +/.settings + + +\ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md @@ -0,0 +1,9 @@ +Copyright © 2014 [Ikrom][1]. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + [1]: https://github.com/ikr0m diff --git a/app/assets/javascripts/mime-js.coffee b/app/assets/javascripts/mime-js.coffee @@ -0,0 +1,113 @@ +### + mime-js.js 0.1 + 2014-10-18 + + By Ikrom, https://github.com/ikr0m + License: X11/MIT +### + + +window.createMimeMessage = (mail) -> + getBoundary = -> + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2) + + createPlain = (textContent = '') -> + '\nContent-Type: text/plain; charset=UTF-8' + + '\nContent-Transfer-Encoding: base64' + + '\n\n' + (Base64.encode textContent, true).replace(/.{76}/g, "$&\n") + + createHtml = (msg) -> + htmlContent = msg.body || "" + htmlContent = htmlContent.replace(/&/g, '&amp;').replace(/</g, + '&lt;').replace(/>/, '&gt;').replace(/\n/g, '\n<br/>') + + htmlContent = '<div>' + htmlContent + '</div>' + '\nContent-Type: text/html; charset=UTF-8' + + '\nContent-Transfer-Encoding: base64' + + '\n\n' + (Base64.encode htmlContent, true).replace(/.{76}/g, "$&\n") + + createAlternative = (text, html) -> + boundary = getBoundary() + + '\nContent-Type: multipart/alternative; boundary=' + boundary + + '\n\n--' + boundary + text + + '\n\n--' + boundary + html + + '\n\n--' + boundary + '--' + + createCids = (cids) -> + return if !cids + cidArr = [] + for cid in cids + type = cid.type + name = cid.name + base64 = cid.base64 + id = getBoundary() + + cidArr.push '\nContent-Type: ' + type + '; name=\"' + name + '\"' + + '\nContent-Transfer-Encoding: base64' + + '\nContent-ID: <' + id + '>' + + '\nX-Attachment-Id: ' + id + + '\n\n' + base64 + cidArr + + createRelated = (alternative, cids = []) -> + boundary = getBoundary() + + relatedStr = '\nContent-Type: multipart/related; boundary=' + boundary + + '\n\n--' + boundary + alternative + for cid in cids + relatedStr += ('\n--' + boundary + cid) + + relatedStr + '\n--' + boundary + '--' + + createAttaches = (attaches) -> + return if !attaches + result = [] + for attach in attaches + type = attach.type + name = attach.name + base64 = attach.base64 + id = getBoundary() + + result.push '\nContent-Type: ' + type + '; name=\"' + name + '\"' + + '\nContent-Disposition: attachment; filename=\"' + name + '\"' + + '\nContent-Transfer-Encoding: base64' + + '\nX-Attachment-Id: ' + id + + '\n\n' + base64 + result + + createMixed = (related, attaches = []) -> + boundary = getBoundary() + if mail.subject + subject = '=?UTF-8?B?' + Base64.encode(mail.subject, true) + '?=' + subject ?= '' + mailFromName = '=?UTF-8?B?' + Base64.encode(mail.fromName || "", + true) + '?=' + date = (new Date().toGMTString()).replace(/GMT|UTC/gi, '+0000') + mimeStr = 'MIME-Version: 1.0' + + '\nDate: ' + date + + '\nDelivered-To: ' + mail.to + + '\nMessage-ID: <' + getBoundary() + '@mail.your-domain.com>' + + '\nSubject: ' + subject + + '\nFrom: ' + mailFromName + ' <' + mail.from + '>' + + '\nTo: ' + mail.to + + '\nContent-Type: multipart/mixed; boundary=' + boundary + + '\n\n--' + boundary + related + + for attach in attaches + mimeStr += ('\n--' + boundary + attach) + + (mimeStr + '\n--' + boundary + '--').replace /\n/g, '\r\n' + + try + plain = createPlain mail.body + htm = createHtml mail + alternative = createAlternative plain, htm + cids = createCids mail.cids + related = createRelated alternative, cids + attaches = createAttaches mail.attaches + + createMixed related, attaches + + catch err + throw new Error err diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala @@ -0,0 +1,12 @@ +package controllers + +import play.api._ +import play.api.mvc._ + +object Application extends Controller { + + def index = Action { + Ok(views.html.index()) + } + +} +\ No newline at end of file diff --git a/app/views/index.scala.html b/app/views/index.scala.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> + +<html> +<head> + <title>MIME JS</title> + <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")"> + <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")"> + <script src="@routes.Assets.at("javascripts/base64.js")" type="text/javascript"></script> + <script src="@routes.Assets.at("javascripts/mime-js.js")" type="text/javascript"></script> + +</head> +<body> + <b>Mail obj:</b><div id="mail"></div> + <hr> + <div id="mime"></div> + + <script type="application/javascript"> + var mail = { + "to": "email1@@example.com", + "subject": "Today is rainy", + "fromName": "John Smith", + "from": "john.smith@@mail.com", + "body": "Sample body text", + "cids": [], + "attaches" : [] + } + document.getElementById("mail" ).innerText = JSON.stringify(mail); + document.getElementById("mime" ).innerText = createMimeMessage(mail); + </script> +</body> +</html> diff --git a/build.sbt b/build.sbt @@ -0,0 +1,7 @@ +import play.Project._ + +name := "Mime-JS" + +version := "1.0" + +playScalaSettings +\ No newline at end of file diff --git a/conf/application.conf b/conf/application.conf @@ -0,0 +1,59 @@ +# This is the main configuration file for the application. +# ~~~~~ + +# Secret key +# ~~~~~ +# The secret key is used to secure cryptographics functions. +# If you deploy your application to several instances be sure to use the same key! +application.secret="%APPLICATION_SECRET%" + +# The application languages +# ~~~~~ +application.langs="en" + +# Global object class +# ~~~~~ +# Define the Global object class for this application. +# Default to Global in the root package. +# application.global=Global + +# Router +# ~~~~~ +# Define the Router object to use for this application. +# This router will be looked up first when the application is starting up, +# so make sure this is the entry point. +# Furthermore, it's assumed your route file is named properly. +# So for an application router like `my.application.Router`, +# you may need to define a router file `conf/my.application.routes`. +# Default to Routes in the root package (and conf/routes) +# application.router=my.application.Routes + +# Database configuration +# ~~~~~ +# You can declare as many datasources as you want. +# By convention, the default datasource is named `default` +# +# db.default.driver=org.h2.Driver +# db.default.url="jdbc:h2:mem:play" +# db.default.user=sa +# db.default.password="" + +# Evolutions +# ~~~~~ +# You can disable evolutions if needed +# evolutionplugin=disabled + +# Logger +# ~~~~~ +# You can also configure logback (http://logback.qos.ch/), +# by providing an application-logger.xml file in the conf directory. + +# Root logger: +logger.root=ERROR + +# Logger used by the framework: +logger.play=INFO + +# Logger provided to your application: +logger.application=DEBUG + diff --git a/conf/routes b/conf/routes @@ -0,0 +1,11 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# ~~~~ + +# Home page +GET / controllers.Application.index + +# Map static resources from the /public folder to the /assets URL path +GET /assets/*file controllers.Assets.at(path="/public", file) + + +\ No newline at end of file diff --git a/project/build.properties b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.0 +\ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt @@ -0,0 +1,5 @@ +logLevel := Level.Warn + +resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" + +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.1") +\ No newline at end of file diff --git a/public/images/favicon.png b/public/images/favicon.png Binary files differ. diff --git a/public/javascripts/base64.js b/public/javascripts/base64.js @@ -0,0 +1,125 @@ +var Base64 = { + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input, needEncoding) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + if (needEncoding) { + input = Base64._utf8_encode(input); + } + + while (i < input.length) { + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + } + + return output; + }, + + // public method for decoding + decode : function (input, needDecoding) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + } + + if (needDecoding) { + output = Base64._utf8_decode(output); + } + + return output; + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + c = utftext.charCodeAt(i); + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +}; +\ No newline at end of file diff --git a/public/javascripts/mime-js.js b/public/javascripts/mime-js.js @@ -0,0 +1,115 @@ + +/* + mime-js.js 0.1 + 2014-10-18 + + By Ikrom, https://github.com/ikr0m + License: X11/MIT +*/ + + +(function() { + + window.createMimeMessage = function(mail) { + var alternative, attaches, cids, createAlternative, createAttaches, createCids, createHtml, createMixed, createPlain, createRelated, getBoundary, htm, plain, related; + getBoundary = function() { + return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2); + }; + createPlain = function(textContent) { + if (textContent == null) { + textContent = ''; + } + return '\nContent-Type: text/plain; charset=UTF-8' + '\nContent-Transfer-Encoding: base64' + '\n\n' + (Base64.encode(textContent, true)).replace(/.{76}/g, "$&\n"); + }; + createHtml = function(msg) { + var htmlContent; + htmlContent = msg.body || ""; + htmlContent = htmlContent.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/, '&gt;').replace(/\n/g, '\n<br/>'); + htmlContent = '<div>' + htmlContent + '</div>'; + return '\nContent-Type: text/html; charset=UTF-8' + '\nContent-Transfer-Encoding: base64' + '\n\n' + (Base64.encode(htmlContent, true)).replace(/.{76}/g, "$&\n"); + }; + createAlternative = function(text, html) { + var boundary; + boundary = getBoundary(); + return '\nContent-Type: multipart/alternative; boundary=' + boundary + '\n\n--' + boundary + text + '\n\n--' + boundary + html + '\n\n--' + boundary + '--'; + }; + createCids = function(cids) { + var base64, cid, cidArr, id, name, type, _i, _len; + if (!cids) { + return; + } + cidArr = []; + for (_i = 0, _len = cids.length; _i < _len; _i++) { + cid = cids[_i]; + type = cid.type; + name = cid.name; + base64 = cid.base64; + id = getBoundary(); + cidArr.push('\nContent-Type: ' + type + '; name=\"' + name + '\"' + '\nContent-Transfer-Encoding: base64' + '\nContent-ID: <' + id + '>' + '\nX-Attachment-Id: ' + id + '\n\n' + base64); + } + return cidArr; + }; + createRelated = function(alternative, cids) { + var boundary, cid, relatedStr, _i, _len; + if (cids == null) { + cids = []; + } + boundary = getBoundary(); + relatedStr = '\nContent-Type: multipart/related; boundary=' + boundary + '\n\n--' + boundary + alternative; + for (_i = 0, _len = cids.length; _i < _len; _i++) { + cid = cids[_i]; + relatedStr += '\n--' + boundary + cid; + } + return relatedStr + '\n--' + boundary + '--'; + }; + createAttaches = function(attaches) { + var attach, base64, id, name, result, type, _i, _len; + if (!attaches) { + return; + } + result = []; + for (_i = 0, _len = attaches.length; _i < _len; _i++) { + attach = attaches[_i]; + type = attach.type; + name = attach.name; + base64 = attach.base64; + id = getBoundary(); + result.push('\nContent-Type: ' + type + '; name=\"' + name + '\"' + '\nContent-Disposition: attachment; filename=\"' + name + '\"' + '\nContent-Transfer-Encoding: base64' + '\nX-Attachment-Id: ' + id + '\n\n' + base64); + } + return result; + }; + createMixed = function(related, attaches) { + var attach, boundary, date, mailFromName, mimeStr, subject, _i, _len; + if (attaches == null) { + attaches = []; + } + boundary = getBoundary(); + if (mail.subject) { + subject = '=?UTF-8?B?' + Base64.encode(mail.subject, true) + '?='; + } + if (subject == null) { + subject = ''; + } + mailFromName = '=?UTF-8?B?' + Base64.encode(mail.fromName || "", true) + '?='; + date = (new Date().toGMTString()).replace(/GMT|UTC/gi, '+0000'); + mimeStr = 'MIME-Version: 1.0' + '\nDate: ' + date + '\nDelivered-To: ' + mail.to + '\nMessage-ID: <' + getBoundary() + '@mail.your-domain.com>' + '\nSubject: ' + subject + '\nFrom: ' + mailFromName + ' <' + mail.from + '>' + '\nTo: ' + mail.to + '\nContent-Type: multipart/mixed; boundary=' + boundary + '\n\n--' + boundary + related; + for (_i = 0, _len = attaches.length; _i < _len; _i++) { + attach = attaches[_i]; + mimeStr += '\n--' + boundary + attach; + } + return (mimeStr + '\n--' + boundary + '--').replace(/\n/g, '\r\n'); + }; + try { + plain = createPlain(mail.body); + htm = createHtml(mail); + alternative = createAlternative(plain, htm); + cids = createCids(mail.cids); + related = createRelated(alternative, cids); + attaches = createAttaches(mail.attaches); + return createMixed(related, attaches); + } catch (err) { + throw new Error(err); + } + }; + +}).call(this); diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css