diff options
author | grant-kun <[email protected]> | 2022-10-13 13:18:42 -0500 |
---|---|---|
committer | grant-kun <[email protected]> | 2022-10-13 13:18:42 -0500 |
commit | 26d50b8e63f79e48652840a54567b059d1f73766 (patch) | |
tree | 08d81de8bf558e80d0fde43e6c400650ae4786e8 | |
parent | afdd65e44c3a1a89d1aaa1c089508bd86477d727 (diff) |
adding stuff
-rw-r--r-- | html/mail.html | 38 | ||||
-rw-r--r-- | index.ts | 67 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | readme.md | 4 | ||||
-rw-r--r-- | src/autolink.js | 245 | ||||
-rw-r--r-- | src/quoted-printable.js | 153 |
6 files changed, 486 insertions, 24 deletions
diff --git a/html/mail.html b/html/mail.html index f726505..6c66f3a 100644 --- a/html/mail.html +++ b/html/mail.html @@ -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] @@ -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 diff --git a/package.json b/package.json index 32dba1c..1afa863 100644 --- a/package.json +++ b/package.json @@ -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) diff --git a/src/autolink.js b/src/autolink.js new file mode 100644 index 0000000..c04a0d2 --- /dev/null +++ b/src/autolink.js @@ -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 diff --git a/src/quoted-printable.js b/src/quoted-printable.js new file mode 100644 index 0000000..87f2329 --- /dev/null +++ b/src/quoted-printable.js @@ -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)); |