adding stuff
This commit is contained in:
parent
afdd65e44c
commit
26d50b8e63
|
@ -4,19 +4,23 @@
|
|||
<title>mail</title>
|
||||
<script src="/src/bundle.js"></script>
|
||||
<script src=" https://unpkg.com/showdown/dist/showdown.min.js"></script>
|
||||
<script src='/src/autolink.js'></script>
|
||||
<script src='/src/quoted-printable.js'></script>
|
||||
<style>
|
||||
.split {
|
||||
height: 90%;
|
||||
width: 50%;
|
||||
width: 51%;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
overflow-x: hidden;
|
||||
padding-top: 10px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
body {
|
||||
background-color: #444444;
|
||||
}
|
||||
|
||||
|
||||
svg.tea {
|
||||
|
@ -278,14 +282,21 @@
|
|||
}
|
||||
};
|
||||
let emails = []
|
||||
let bod
|
||||
let html = false
|
||||
function update() {
|
||||
//console.log('hi')
|
||||
let promie = new Promise((resolve, reject) => {
|
||||
sendenc('/get/update', { 'user': 'root', 'requested': 0, 'login_key': getCookie('login_key') }).then(res => {
|
||||
res = JSON.parse(res)
|
||||
console.log('parsed')
|
||||
emails = res.reverse()
|
||||
emails = res.messages.reverse()
|
||||
//console.log(res)
|
||||
console.log(res)
|
||||
bod = res.bod
|
||||
if (res.bod == 'body[2]') {
|
||||
html = true
|
||||
}
|
||||
umail()
|
||||
resolve(res)
|
||||
})
|
||||
|
@ -309,13 +320,18 @@
|
|||
let mypriv, mypub, pub, kekw
|
||||
let preview = -1
|
||||
var converter = new showdown.Converter()
|
||||
|
||||
async function lm(index) {
|
||||
let m = ((emails[index]['body[1]']))
|
||||
var linkify = m.replace(/(<a href=")?((https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)))(">(.*)<\/a>)?/gi, function () {
|
||||
return '<a href="' + arguments[2] + '">' + (arguments[7] || arguments[2]) + '</a>'
|
||||
});
|
||||
let m
|
||||
if (html) {
|
||||
m = autoLink((emails[index]['body[2]']))
|
||||
} else {
|
||||
m = converter.makeHtml(autoLink((quotedPrintable.decode(emails[index]['body[0]']))))
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('box').style.display = 'block'
|
||||
document.getElementById('view').innerHTML = converter.makeHtml(linkify)
|
||||
document.getElementById('view').innerHTML = (m)
|
||||
|
||||
|
||||
}
|
||||
|
@ -329,7 +345,7 @@
|
|||
c = '#395B64'
|
||||
}
|
||||
evo = !evo
|
||||
ret += '<div onclick="lm(' + emails.indexOf(email) + ')" style="white-space: pre-wrap;color:#A5C9CA;border-radius:10px;max-width:40%;min-width:400px;padding:20px;background-color:' + c + ';">'
|
||||
ret += '<div onclick="lm(' + emails.indexOf(email) + ')" style="height:10%;white-space: pre-wrap;color:#A5C9CA;border-radius:5px;max-width:40%;min-width:400px;padding:20px;background-color:' + c + ';">'
|
||||
ret += '<tt><b><font size="4">sub:' + email.envelope.subject + '</font></b></br>frm:' + email.envelope.from[0].address + '</br><sub style="color:#E7F6F2;">' + email.envelope.date + '</sub></tt></br></div><div style="height:2px;"></div>'
|
||||
}
|
||||
|
||||
|
@ -345,7 +361,7 @@
|
|||
if (getCookie('login_key') == null) {
|
||||
window.location.href = '/'
|
||||
}
|
||||
await init()
|
||||
await update()
|
||||
//return
|
||||
//document.getElementsByClassName('loading')[0].style.display = 'none'
|
||||
let xx = document.getElementsByClassName('card')[0]
|
||||
|
|
67
index.ts
67
index.ts
|
@ -1,4 +1,5 @@
|
|||
import { readFileSync, writeFileSync } from "fs"
|
||||
import { LogLevel } from "node-ts";
|
||||
var privateKey = readFileSync('certs/selfsigned.key', 'utf8');
|
||||
var certificate = readFileSync('certs/selfsigned.crt', 'utf8');
|
||||
var http = require('http');
|
||||
|
@ -13,7 +14,33 @@ app.use(bodyParser.json());
|
|||
const NodeRSA = require('node-rsa');
|
||||
var ip = require("ip")
|
||||
var crypt = require('crypto');
|
||||
|
||||
Object.defineProperty(global, '__stack', {
|
||||
get: function() {
|
||||
var orig = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = function(_, stack) {
|
||||
return stack;
|
||||
};
|
||||
var err = new Error;
|
||||
Error.captureStackTrace(err, arguments.callee);
|
||||
var stack = err.stack;
|
||||
Error.prepareStackTrace = orig;
|
||||
return stack;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(global, '__line', {
|
||||
get: function() {
|
||||
// @ts-ignore
|
||||
return __stack[1].getLineNumber();
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(global, '__function', {
|
||||
get: function() {
|
||||
// @ts-ignore
|
||||
return __stack[1].getFunctionName();
|
||||
}
|
||||
});
|
||||
const IV = "5183666c72eec9e4"; //!not really sure what this is lol
|
||||
//TODO: learn what IV is
|
||||
var encrypt = ((val:any,ENC_KEY:any) => {
|
||||
|
@ -34,7 +61,11 @@ var decrypt = ((encrypted:any,ENC_KEY:any) => {
|
|||
//
|
||||
function log(m:any){
|
||||
var date = new Date;
|
||||
console.log('['+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds()+'] ' + m.toString())
|
||||
let e:any = new Error();
|
||||
let frame = e.stack.split("\n")[2]; // change to 3 for grandparent func
|
||||
let lineNumber = frame.split(":").reverse()[1];
|
||||
let functionName = frame.split(" ")[5];
|
||||
console.log('['+functionName+'/'+lineNumber+'][./index.ts]'+'['+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds()+'] ' + m.toString())
|
||||
}
|
||||
function d(){
|
||||
var date = new Date;
|
||||
|
@ -72,14 +103,23 @@ app.post('/mail/get/update',(req:any,res:any)=>{
|
|||
var client = new ImapClient(mail.host, parseInt(mail.port), {
|
||||
auth: {
|
||||
user: mail.address,
|
||||
pass: mail.creds
|
||||
}
|
||||
pass: mail.creds,
|
||||
|
||||
},logLevel:1000
|
||||
});
|
||||
client.connect().then(()=>{
|
||||
//['uid', 'flags','envelope'] for just header stuff
|
||||
//['uid', 'flags','envelope','body']
|
||||
//body 0 is plani, 1 is html
|
||||
client.listMessages('INBOX', '1:*', ['uid', 'flags','envelope','bodystructure','body[1]' ]).then((messages:any) => {
|
||||
//body 0 is plani, 1 is plain
|
||||
let bo="body[0]"
|
||||
for(let user of users){
|
||||
if(user.name==dec.data.user){
|
||||
if(user.settings.html){
|
||||
bo="body[2]"
|
||||
}
|
||||
}
|
||||
}
|
||||
client.listMessages('INBOX', '1:*', ['uid', 'flags','envelope','bodystructure',bo ]).then((messages:any) => {
|
||||
const skey = new NodeRSA()
|
||||
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
||||
for(let user of users){
|
||||
|
@ -93,8 +133,7 @@ app.post('/mail/get/update',(req:any,res:any)=>{
|
|||
}
|
||||
}
|
||||
//console.log(users,(JSON.stringify(messages)))
|
||||
|
||||
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify(messages),'base64'),enc:true,html:true}))
|
||||
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({messages:messages,bod:bo}),'base64'),enc:true,html:true}))
|
||||
client.close()
|
||||
});
|
||||
})
|
||||
|
@ -164,6 +203,12 @@ app.get('/kanna.txt', (req:any, res:any) => {
|
|||
app.get('/src/bundle.js', (req:any, res:any) => {
|
||||
res.sendFile(__dirname+'/src/bundle.js')
|
||||
})
|
||||
app.get('/src/autolink.js', (req:any, res:any) => {
|
||||
res.sendFile(__dirname+'/src/autolink.js')
|
||||
})
|
||||
app.get('/src/quoted-printable.js', (req:any, res:any) => {
|
||||
res.sendFile(__dirname+'/src/quoted-printable.js')
|
||||
})
|
||||
app.get('/src/lights-out.gif', (req:any, res:any) => {
|
||||
res.sendFile(__dirname+'/src/lights-out.gif')
|
||||
})
|
||||
|
@ -214,13 +259,13 @@ app.use((req:any, res:any, next:any) => {
|
|||
var httpServer = http.createServer(app);
|
||||
var credentials = {key: privateKey, cert: certificate};
|
||||
var httpsServer = https.createServer(credentials, app);
|
||||
app.listen(8008,()=>{
|
||||
app.listen(8008,function local(){
|
||||
log(`kanna is local http://${ip.address()}:8080`)
|
||||
})
|
||||
httpServer.listen(80, () => {
|
||||
httpServer.listen(80, function http() {
|
||||
log(`kanna is on http://${ip.address()} click on me click on me! :3`)
|
||||
})
|
||||
httpsServer.listen(443, () => {
|
||||
httpsServer.listen(443, function https() {
|
||||
log(`kanna is secure now too!! https://${ip.address()}`)
|
||||
})
|
||||
//end
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
"ip": "^1.1.8",
|
||||
"md5": "^2.3.0",
|
||||
"node-rsa": "^1.1.1",
|
||||
"openpgp": "^5.5.0"
|
||||
"openpgp": "^5.5.0",
|
||||
"utf8": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"emailjs-imap-client": "^3.1.0"
|
||||
|
|
|
@ -70,10 +70,12 @@ visit the [main git](https://git.disroot.org/grantsquires/kanna-site) or the [gi
|
|||
- [ ] draft
|
||||
- [ ] forward
|
||||
- [ ] sender info
|
||||
- [ ] allow for email account adding (soon)
|
||||
- [ ] allow for email account adding
|
||||
- [ ] docs
|
||||
- [ ] extra pages
|
||||
- [x] 404 page
|
||||
- [ ] sub domains
|
||||
- [ ] make my own libs
|
||||
- [ ] encryption (md5 hashing, and rsa)
|
||||
|
||||
FeMail (iron mail)
|
||||
|
|
245
src/autolink.js
Normal file
245
src/autolink.js
Normal file
|
@ -0,0 +1,245 @@
|
|||
const re = {
|
||||
http: /.*?:\/\//g,
|
||||
url: /(\s|^)((https?|ftp):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\w-\/\?\=\#\.])*/gi,
|
||||
image: /\.(jpe?g|png|gif)$/,
|
||||
email:
|
||||
/(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/gi,
|
||||
cloudmusic: /http:\/\/music\.163\.com\/#\/song\?id=(\d+)/i,
|
||||
kickstarter:
|
||||
/(https?:\/\/www\.kickstarter\.com\/projects\/\d+\/[a-zA-Z0-9_-]+)(\?\w+\=\w+)?/i,
|
||||
youtube:
|
||||
/https?:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)(\?\w+\=\w+)?/i,
|
||||
vimeo:
|
||||
/https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/[^\/]*\/videos\/|album\/\d+\/video\/|video\/|)(\d+)(?:$|\/|\?)/i,
|
||||
youku:
|
||||
/https?:\/\/v\.youku\.com\/v_show\/id_([a-zA-Z0-9_\=-]+).html(\?\w+\=\w+)?(\#\w+)?/i,
|
||||
};
|
||||
/**
|
||||
* AutoLink constructor function
|
||||
*
|
||||
* @param {String} text
|
||||
* @param {Object} options
|
||||
* @constructor
|
||||
*/
|
||||
function AutoLink(string, options = {}) {
|
||||
this.string = options.safe ? safe_tags_replace(string) : string;
|
||||
this.options = options;
|
||||
this.attrs = "";
|
||||
this.linkAttr = "";
|
||||
this.imageAttr = "";
|
||||
if (this.options.sharedAttr) {
|
||||
this.attrs = getAttr(this.options.sharedAttr);
|
||||
}
|
||||
if (this.options.linkAttr) {
|
||||
this.linkAttr = getAttr(this.options.linkAttr);
|
||||
}
|
||||
if (this.options.imageAttr) {
|
||||
this.imageAttr = getAttr(this.options.imageAttr);
|
||||
}
|
||||
}
|
||||
|
||||
AutoLink.prototype = {
|
||||
constructor: AutoLink,
|
||||
/**
|
||||
* call relative functions to parse url/email/image
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
parse: function () {
|
||||
var shouldReplaceImage = defaultTrue(this.options.image);
|
||||
var shouldRelaceEmail = defaultTrue(this.options.email);
|
||||
var shouldRelaceBr = defaultTrue(this.options.br);
|
||||
|
||||
var result = "";
|
||||
if (!shouldReplaceImage) {
|
||||
result = this.string.replace(re.url, this.formatURLMatch.bind(this));
|
||||
} else {
|
||||
result = this.string.replace(re.url, this.formatIMGMatch.bind(this));
|
||||
}
|
||||
if (shouldRelaceEmail) {
|
||||
result = this.formatEmailMatch.call(this, result);
|
||||
}
|
||||
if (shouldRelaceBr) {
|
||||
result = result.replace(/\r?\n/g, "<br />");
|
||||
}
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* @param {String} match
|
||||
* @returns {String} Offset 1
|
||||
*/
|
||||
formatURLMatch: function (match, p1) {
|
||||
match = prepHTTP(match.trim());
|
||||
if (this.options.cloudmusic || this.options.embed) {
|
||||
if (match.indexOf("music.163.com/#/song?id=") > -1) {
|
||||
return match.replace(
|
||||
re.cloudmusic,
|
||||
p1 +
|
||||
'<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="http://music.163.com/outchain/player?type=2&id=$1&auto=1&height=66"></iframe>'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.options.kickstarter || this.options.embed) {
|
||||
if (re.kickstarter.test(match)) {
|
||||
return match.replace(
|
||||
re.kickstarter,
|
||||
p1 +
|
||||
'<iframe width="480" height="360" src="$1/widget/video.html" frameborder="0" scrolling="no"> </iframe>'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.options.vimeo || this.options.embed) {
|
||||
if (re.vimeo.test(match)) {
|
||||
return match.replace(
|
||||
re.vimeo,
|
||||
p1 +
|
||||
'<iframe width="500" height="281" src="https://player.vimeo.com/video/$1" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.options.youtube || this.options.embed) {
|
||||
if (re.youtube.test(match)) {
|
||||
return match.replace(
|
||||
re.youtube,
|
||||
p1 +
|
||||
'<iframe width="560" height="315" src="https://www.youtube.com/embed/$1" frameborder="0" allowfullscreen></iframe>'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.options.youku || this.options.embed) {
|
||||
if (re.youku.test(match)) {
|
||||
return match.replace(
|
||||
re.youku,
|
||||
p1 +
|
||||
'<iframe height=498 width=510 src="http://player.youku.com/embed/$1" frameborder=0 allowfullscreen></iframe>'
|
||||
);
|
||||
}
|
||||
}
|
||||
var text = this.options.removeHTTP ? removeHTTP(match) : match;
|
||||
return (
|
||||
p1 +
|
||||
'<a href="' +
|
||||
match +
|
||||
'"' +
|
||||
this.attrs +
|
||||
this.linkAttr +
|
||||
">" +
|
||||
text +
|
||||
"</a>"
|
||||
);
|
||||
},
|
||||
/**
|
||||
* @param {String} match
|
||||
* @param {String} Offset 1
|
||||
*/
|
||||
formatIMGMatch: function (match, p1) {
|
||||
match = match.trim();
|
||||
var isIMG = re.image.test(match);
|
||||
if (isIMG) {
|
||||
return (
|
||||
p1 +
|
||||
'<img src="' +
|
||||
prepHTTP(match.trim()) +
|
||||
'"' +
|
||||
this.attrs +
|
||||
this.imageAttr +
|
||||
"/>"
|
||||
);
|
||||
}
|
||||
return this.formatURLMatch(match, p1);
|
||||
},
|
||||
/**
|
||||
* @param {String} text
|
||||
*/
|
||||
formatEmailMatch: function (text) {
|
||||
return text.replace(
|
||||
re.email,
|
||||
'<a href="mailto:$&"' + this.attrs + this.linkAttr + ">$&</a>"
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* return true if undefined
|
||||
* else return itself
|
||||
*
|
||||
* @param {Boolean} val
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function defaultTrue(val) {
|
||||
return typeof val === "undefined" ? true : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse attrs from object
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @returns {Stirng}
|
||||
*/
|
||||
function getAttr(obj) {
|
||||
var attr = "";
|
||||
for (var key in obj) {
|
||||
if (key) {
|
||||
attr += " " + key + '="' + obj[key] + '"';
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} url
|
||||
* @returns {String}
|
||||
*/
|
||||
function prepHTTP(url) {
|
||||
if (url.substring(0, 4) !== "http" && url.substring(0, 2) !== "//") {
|
||||
return "http://" + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} url
|
||||
* @returns {String}
|
||||
*/
|
||||
function removeHTTP(url) {
|
||||
return url.replace(re.http, "");
|
||||
}
|
||||
|
||||
var tagsToReplace = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace tag if should be replace
|
||||
*
|
||||
* @param {String} tag
|
||||
* @returns {String}
|
||||
*/
|
||||
function replaceTag(tag) {
|
||||
return tagsToReplace[tag] || tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make string safe by replacing html tag
|
||||
*
|
||||
* @param {String} str
|
||||
* @returns {String}
|
||||
*/
|
||||
function safe_tags_replace(str) {
|
||||
return str.replace(/[&<>]/g, replaceTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* return an instance of AutoLink
|
||||
*
|
||||
* @param {String} string
|
||||
* @param {Object} options
|
||||
* @returns {Object}
|
||||
*/
|
||||
function autoLink(string, options) {
|
||||
return new AutoLink(string, options).parse();
|
||||
}
|
||||
|
||||
//export default autoLink
|
153
src/quoted-printable.js
Normal file
153
src/quoted-printable.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*! https://mths.be/quoted-printable v<%= version %> by @mathias | MIT license */
|
||||
;(function(root) {
|
||||
|
||||
// Detect free variables `exports`.
|
||||
var freeExports = typeof exports == 'object' && exports;
|
||||
|
||||
// Detect free variable `module`.
|
||||
var freeModule = typeof module == 'object' && module &&
|
||||
module.exports == freeExports && module;
|
||||
|
||||
// Detect free variable `global`, from Node.js or Browserified code, and use
|
||||
// it as `root`.
|
||||
var freeGlobal = typeof global == 'object' && global;
|
||||
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
|
||||
root = freeGlobal;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var stringFromCharCode = String.fromCharCode;
|
||||
var decode = function(input) {
|
||||
return input
|
||||
// https://tools.ietf.org/html/rfc2045#section-6.7, rule 3:
|
||||
// “Therefore, when decoding a `Quoted-Printable` body, any trailing white
|
||||
// space on a line must be deleted, as it will necessarily have been added
|
||||
// by intermediate transport agents.”
|
||||
.replace(/[\t\x20]$/gm, '')
|
||||
// Remove hard line breaks preceded by `=`. Proper `Quoted-Printable`-
|
||||
// encoded data only contains CRLF line endings, but for compatibility
|
||||
// reasons we support separate CR and LF too.
|
||||
.replace(/=(?:\r\n?|\n|$)/g, '')
|
||||
// Decode escape sequences of the form `=XX` where `XX` is any
|
||||
// combination of two hexidecimal digits. For optimal compatibility,
|
||||
// lowercase hexadecimal digits are supported as well. See
|
||||
// https://tools.ietf.org/html/rfc2045#section-6.7, note 1.
|
||||
.replace(/=([a-fA-F0-9]{2})/g, function($0, $1) {
|
||||
var codePoint = parseInt($1, 16);
|
||||
return stringFromCharCode(codePoint);
|
||||
});
|
||||
};
|
||||
|
||||
var handleTrailingCharacters = function(string) {
|
||||
return string
|
||||
.replace(/\x20$/, '=20') // Handle trailing space.
|
||||
.replace(/\t$/, '=09') // Handle trailing tab.
|
||||
};
|
||||
|
||||
var regexUnsafeSymbols = /<%= unsafeSymbols %>/g;
|
||||
var encode = function(string) {
|
||||
|
||||
// Encode symbols that are definitely unsafe (i.e. unsafe in any context).
|
||||
var encoded = string.replace(regexUnsafeSymbols, function(symbol) {
|
||||
if (symbol > '\xFF') {
|
||||
throw RangeError(
|
||||
'`quotedPrintable.encode()` expects extended ASCII input only. ' +
|
||||
'Don\u2019t forget to encode the input first using a character ' +
|
||||
'encoding like UTF-8.'
|
||||
);
|
||||
}
|
||||
var codePoint = symbol.charCodeAt(0);
|
||||
var hexadecimal = codePoint.toString(16).toUpperCase();
|
||||
return '=' + ('0' + hexadecimal).slice(-2);
|
||||
});
|
||||
|
||||
// Limit lines to 76 characters (not counting the CRLF line endings).
|
||||
var lines = encoded.split(/\r\n?|\n/g);
|
||||
var lineIndex = -1;
|
||||
var lineCount = lines.length;
|
||||
var result = [];
|
||||
while (++lineIndex < lineCount) {
|
||||
var line = lines[lineIndex];
|
||||
// Leave room for the trailing `=` for soft line breaks.
|
||||
var LINE_LENGTH = 75;
|
||||
var index = 0;
|
||||
var length = line.length;
|
||||
while (index < length) {
|
||||
var buffer = encoded.slice(index, index + LINE_LENGTH);
|
||||
// If this line ends with `=`, optionally followed by a single uppercase
|
||||
// hexadecimal digit, we broke an escape sequence in half. Fix it by
|
||||
// moving these characters to the next line.
|
||||
if (/=$/.test(buffer)) {
|
||||
buffer = buffer.slice(0, LINE_LENGTH - 1);
|
||||
index += LINE_LENGTH - 1;
|
||||
} else if (/=[A-F0-9]$/.test(buffer)) {
|
||||
buffer = buffer.slice(0, LINE_LENGTH - 2);
|
||||
index += LINE_LENGTH - 2;
|
||||
} else {
|
||||
index += LINE_LENGTH;
|
||||
}
|
||||
result.push(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode space and tab characters at the end of encoded lines. Note that
|
||||
// with the current implementation, this can only occur at the very end of
|
||||
// the encoded string — every other line ends with `=` anyway.
|
||||
var lastLineLength = buffer.length;
|
||||
if (/[\t\x20]$/.test(buffer)) {
|
||||
// There’s a space or a tab at the end of the last encoded line. Remove
|
||||
// this line from the `result` array, as it needs to change.
|
||||
result.pop();
|
||||
if (lastLineLength + 2 <= LINE_LENGTH + 1) {
|
||||
// It’s possible to encode the character without exceeding the line
|
||||
// length limit.
|
||||
result.push(
|
||||
handleTrailingCharacters(buffer)
|
||||
);
|
||||
} else {
|
||||
// It’s not possible to encode the character without exceeding the line
|
||||
// length limit. Remvoe the character from the line, and insert a new
|
||||
// line that contains only the encoded character.
|
||||
result.push(
|
||||
buffer.slice(0, lastLineLength - 1),
|
||||
handleTrailingCharacters(
|
||||
buffer.slice(lastLineLength - 1, lastLineLength)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// `Quoted-Printable` uses CRLF.
|
||||
return result.join('=\r\n');
|
||||
};
|
||||
|
||||
var quotedPrintable = {
|
||||
'encode': encode,
|
||||
'decode': decode,
|
||||
'version': '<%= version %>'
|
||||
};
|
||||
|
||||
// Some AMD build optimizers, like r.js, check for specific condition patterns
|
||||
// like the following:
|
||||
if (
|
||||
typeof define == 'function' &&
|
||||
typeof define.amd == 'object' &&
|
||||
define.amd
|
||||
) {
|
||||
define(function() {
|
||||
return quotedPrintable;
|
||||
});
|
||||
} else if (freeExports && !freeExports.nodeType) {
|
||||
if (freeModule) { // in Node.js, io.js, or RingoJS v0.8.0+
|
||||
freeModule.exports = quotedPrintable;
|
||||
} else { // in Narwhal or RingoJS v0.7.0-
|
||||
for (var key in quotedPrintable) {
|
||||
quotedPrintable.hasOwnProperty(key) && (freeExports[key] = quotedPrintable[key]);
|
||||
}
|
||||
}
|
||||
} else { // in Rhino or a web browser
|
||||
root.quotedPrintable = quotedPrintable;
|
||||
}
|
||||
|
||||
}(this));
|
Loading…
Reference in a new issue