425 lines
13 KiB
TypeScript
425 lines
13 KiB
TypeScript
import { readFileSync, writeFileSync } from "fs"
|
|
var privateKey = readFileSync('certs/selfsigned.key', 'utf8');
|
|
var certificate = readFileSync('certs/selfsigned.crt', 'utf8');
|
|
var http = require('http');
|
|
var https = require('https');
|
|
const express = require('express')
|
|
const app = express()
|
|
import { Sequelize, Model, DataTypes } from 'sequelize';
|
|
//const port = 8008
|
|
const fs = require('fs')
|
|
const bodyParser = require("body-parser");
|
|
app.use(bodyParser.urlencoded({ extended: false }));
|
|
app.use(bodyParser.json());
|
|
const NodeRSA = require('node-rsa');
|
|
var ip = require("ip")
|
|
var crypt = require('crypto');
|
|
app.use('/favicon.ico', express.static('/src/favicon.ico'))
|
|
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 sequelize = new Sequelize({
|
|
dialect: 'sqlite',
|
|
storage: 'data/user.sqlite',
|
|
logging: false
|
|
|
|
});
|
|
let User=sequelize.define('user',{
|
|
"html":DataTypes.BOOLEAN,
|
|
"test":DataTypes.TEXT,
|
|
"name":DataTypes.TEXT,
|
|
"hash":DataTypes.TEXT,
|
|
"sudo":DataTypes.BOOLEAN,
|
|
"last_login":DataTypes.TEXT,
|
|
"alias":DataTypes.TEXT,
|
|
"login_key":DataTypes.TEXT,
|
|
"mail":DataTypes.TEXT
|
|
})
|
|
sequelize.authenticate()
|
|
/*
|
|
User.sync({force:true}).then(()=>{
|
|
User.create(
|
|
{"html":false,"name":"root","hash":"OTVYg/fHYeVbtyrusPl8fV+zQcp1ImjzbP+3Cy+3lk14fl2icYhzlULKtbTpOx4E","sudo":true,"last_login":"","alias":"root","login_key":""
|
|
,"mail":""}
|
|
).then((i:any)=>{
|
|
i.save()
|
|
})
|
|
})*/
|
|
const IV = "5183666c72eec9e4"; //!increase size eventually
|
|
var encrypt = ((val:any,ENC_KEY:any) => {
|
|
let cipher = crypt.createCipheriv('aes-256-cbc', ENC_KEY, IV);
|
|
let encrypted = cipher.update(val, 'utf8', 'base64');
|
|
encrypted += cipher.final('base64');
|
|
return encrypted;
|
|
});
|
|
var decrypt = ((encrypted:any,ENC_KEY:any) => {
|
|
try{
|
|
let decipher = crypt.createDecipheriv('aes-256-cbc', ENC_KEY, IV);
|
|
let decrypted = decipher.update(encrypted, 'base64', 'utf8');
|
|
return (decrypted + decipher.final('utf8'));
|
|
} catch(err){
|
|
return false;
|
|
}
|
|
});
|
|
//
|
|
function log(m:any){
|
|
var date = new Date;
|
|
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('\x1b[33m['+functionName+'/'+lineNumber+'][./index.ts]'+'['+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds()+'] \x1b[32m' + m.toString() + '\x1b[0m')
|
|
}
|
|
function d(){
|
|
var date = new Date;
|
|
return(date.getHours()+''+date.getMinutes()+''+date.getSeconds())
|
|
|
|
}
|
|
interface keyring{
|
|
[sid: string]: {
|
|
mypub:string,
|
|
theirpub:string,
|
|
mypriv:string,
|
|
},
|
|
}
|
|
let keyring = {} as keyring
|
|
let key:any;
|
|
var ImapClient = require('emailjs-imap-client').default
|
|
/*
|
|
app.use((req:any, res:any, next:any) => {
|
|
//if(req.get('Host').split('.')[0]=='mail'){}
|
|
//@ts-ignore
|
|
let path = req.url
|
|
Object.defineProperty(req, 'url', {
|
|
get() {
|
|
|
|
return path;
|
|
},
|
|
set(value) {
|
|
path = value;
|
|
}
|
|
})
|
|
if(req.get('Host').split('.')[0]=='mail'){
|
|
if(!req.url.includes('src')&&!req.url.includes('key')&&!req.url.includes('/')&&!req.url.includes('mail')){
|
|
console.log(req.url)
|
|
req.url='/mail'+req.url
|
|
console.log(req.url)
|
|
}
|
|
|
|
}
|
|
next()
|
|
})
|
|
*/ // get subdomains working:(
|
|
|
|
app.post('/mail/get/update',async(req:any,res:any)=>{
|
|
const key = new NodeRSA({b: 1024})
|
|
|
|
key.importKey(keyring[req.body.sid].mypriv,'pkcs1-private')
|
|
let dec:any = JSON.parse((atob(key.decrypt(req.body.data,'base64','base64'))))
|
|
const users:any = await User.findAll();
|
|
let logkey:any,mail:any
|
|
for(let user of users){
|
|
//console.log(user,dec)
|
|
if(user.alias==dec.data.user){
|
|
logkey = (decrypt(user.login_key,dec.data.login_key))
|
|
console.log(logkey)
|
|
let m = decrypt(user.mail,logkey)
|
|
if(m==false){
|
|
mail=''
|
|
} else {
|
|
mail =JSON.parse(decrypt(user.mail,logkey)).emails[parseInt(dec.data.requested)]
|
|
}
|
|
break
|
|
|
|
}
|
|
}
|
|
if(mail==''){
|
|
const skey = new NodeRSA()
|
|
let mail;
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({messages:'reg'}),'base64'),enc:true,html:true}))
|
|
return
|
|
}
|
|
console.log(mail.host)
|
|
/*
|
|
var client = new ImapClient(mail.host, parseInt(mail.port), {
|
|
auth: {
|
|
user: mail.address,
|
|
pass: mail.creds,
|
|
|
|
}
|
|
}).connect().then(()=>{
|
|
console.log('connected')
|
|
//['uid', 'flags','envelope'] for just header stuff
|
|
//['uid', 'flags','envelope','body']
|
|
//body 0 is plani, 1 is plain
|
|
let bo="body[0]"
|
|
for(let user of users){
|
|
if(user.alias==dec.data.user){
|
|
if(user.html){
|
|
bo="body[2]"
|
|
}
|
|
}
|
|
}
|
|
try{
|
|
client.listMessages('INBOX', '1:*', ['uid', 'flags','envelope','bodystructure',bo ]).then((messages:any) => {
|
|
const skey = new NodeRSA()
|
|
let mail;
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
for(let user of users){
|
|
console.log(user)
|
|
if(user.alias==dec.data.user){
|
|
mail = JSON.parse(decrypt(user.mail,logkey))
|
|
mail.emails[parseInt(dec.data.requested)].storage = messages
|
|
user.setDataValue('mail',encrypt(JSON.stringify(mail),logkey))
|
|
user.save()
|
|
User.sync({ alter: true })
|
|
break
|
|
}
|
|
}
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({messages:messages,bod:bo}),'base64'),enc:true,html:true}))
|
|
client.close()
|
|
}).catch((err:any)=>{
|
|
//! if no messages
|
|
console.log('none',err)
|
|
const skey = new NodeRSA()
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({messages:[],bod:bo}),'base64'),enc:true,html:true}))
|
|
client.close()
|
|
});
|
|
} catch(err){
|
|
const skey = new NodeRSA()
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({messages:[],bod:bo}),'base64'),enc:true,html:true}))
|
|
client.close()
|
|
}
|
|
}).catch((err:any)=>{
|
|
console.log(err)
|
|
})
|
|
//deprecated:(*/
|
|
})
|
|
app.post('/mail/del',async(req:any,res:any)=>{
|
|
const key = new NodeRSA({b: 1024})
|
|
|
|
key.importKey(keyring[req.body.sid].mypriv,'pkcs1-private')
|
|
let dec:any = JSON.parse((atob(key.decrypt(req.body.data,'base64','base64'))))
|
|
const users:any = await User.findAll();
|
|
let logkey:any,mail:any;
|
|
for(let user of users){
|
|
//console.log(user,dec)
|
|
if(user.name==dec.data.user){
|
|
logkey = (decrypt(user.login_key,dec.data.login_key))
|
|
mail =JSON.parse(decrypt(user.mail,logkey)).emails[parseInt(dec.data.requested)]
|
|
}
|
|
}
|
|
var client = new ImapClient(mail.host, parseInt(mail.port), {
|
|
auth: {
|
|
user: mail.address,
|
|
pass: mail.creds,
|
|
|
|
},logLevel:1000
|
|
});
|
|
client.connect().then(()=>{
|
|
const skey = new NodeRSA()
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
client.deleteMessages('INBOX',dec.data.index+':'+dec.data.index).then(()=>{
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({'comp':true}),'base64'),enc:true,html:true}))
|
|
client.close()
|
|
})
|
|
|
|
|
|
})
|
|
})
|
|
app.post('/mail/reg',async(req:any,res:any)=>{
|
|
const key = new NodeRSA({b: 1024})
|
|
const skey = new NodeRSA()
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
key.importKey(keyring[req.body.sid].mypriv,'pkcs1-private')
|
|
let dec:any = JSON.parse((atob(key.decrypt(req.body.data,'base64','base64'))))
|
|
const users:any = await User.findAll();
|
|
let logkey:any,mail:any
|
|
for(let user of users){
|
|
if(user.alias==dec.data.user){
|
|
console.log(dec.login_key)
|
|
logkey = await (decrypt(user.login_key,dec.login_key))
|
|
mail=users.indexOf(user)
|
|
user.setDataValue('mail',encrypt(JSON.stringify({'emails':[{
|
|
'address':dec.data.address,
|
|
'host':dec.data.host,
|
|
'port':dec.data.port,
|
|
'creds':dec.data.creds,
|
|
'salt':crypt.randomBytes(64).toString('hex')
|
|
}]}),logkey))
|
|
user.save()
|
|
User.sync()
|
|
break
|
|
}
|
|
}
|
|
})
|
|
app.get('/mail', (req:any, res:any) => {
|
|
res.sendFile(__dirname+'/html/mail.html')
|
|
|
|
})
|
|
app.post('/mail/get/storage',async(req:any,res:any)=>{
|
|
const key = new NodeRSA({b: 1024})
|
|
const skey = new NodeRSA()
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
key.importKey(keyring[req.body.sid].mypriv,'pkcs1-private')
|
|
let dec:any = JSON.parse((atob(key.decrypt(req.body.data,'base64','base64'))))
|
|
const users:any = await User.findAll();
|
|
let logkey:any,mail:any
|
|
for(let user of users){
|
|
if(user.alias==dec.data.user){
|
|
logkey = (decrypt(user.login_key,dec.data.login_key))
|
|
let m = JSON.parse(decrypt(user.mail,logkey)).emails
|
|
if(m==undefined){
|
|
mail=''
|
|
} else {
|
|
mail =JSON.parse(decrypt(user.mail,logkey))
|
|
}
|
|
|
|
}
|
|
}
|
|
if(mail==''){
|
|
const skey = new NodeRSA()
|
|
let mail;
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({messages:'reg'}),'base64'),enc:true,html:true}))
|
|
return
|
|
}
|
|
let d = skey.encrypt((mail.emails[parseInt(dec.data.requested)].storage),'base64')
|
|
res.send(JSON.stringify({data:d,enc:true,html:true}))
|
|
})
|
|
app.get('/mail', (req:any, res:any) => {
|
|
res.sendFile(__dirname+'/html/mail.html')
|
|
|
|
})
|
|
|
|
app.get('/', (req:any, res:any) => {
|
|
res.sendFile(__dirname+"/html/index.html")
|
|
})
|
|
app.get('/kanna.txt', (req:any, res:any) => {
|
|
res.sendFile(__dirname+"/kanna.txt")
|
|
})
|
|
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')
|
|
})
|
|
app.get('/src/kanna.gif', (req:any, res:any) => {
|
|
res.sendFile(__dirname+'/src/kanna.gif')
|
|
})
|
|
app.get('/src/sauce-code-mono.ttf', (req:any, res:any) => {
|
|
res.sendFile(__dirname+'/src/sauce-code-mono.ttf')
|
|
})
|
|
app.get('/home', (req:any, res:any) => {
|
|
res.sendFile(__dirname+'/html/home.html')
|
|
})
|
|
app.post('/pub.key', async (req:{body:{json:boolean,sid:keyof keyring,pub:string}}, res:any) => {
|
|
if(req.body.json){
|
|
|
|
|
|
const key = new NodeRSA({b: 1024});
|
|
keyring[req.body.sid]={mypriv:key.exportKey('pkcs1-private'),
|
|
mypub:key.exportKey('pkcs8-public'),
|
|
theirpub:req.body.pub}
|
|
res.send(key.exportKey('pkcs8-public'))
|
|
}
|
|
})
|
|
|
|
app.post('/login/submit', async (req:{body:{json:boolean,enc:boolean,data:string,sid:keyof keyring}}, res:any) => {
|
|
const key = new NodeRSA({b: 1024})
|
|
|
|
key.importKey(keyring[req.body.sid].mypriv,'pkcs1-private')
|
|
let dec:{user:string,pass:string} = JSON.parse((atob(key.decrypt(req.body.data,'base64','base64'))))
|
|
|
|
const users:any =await User.findAll();
|
|
for(let user of users){
|
|
let use=user as typeof users
|
|
let hash = crypt.createHash('md5').update(dec.pass).digest('hex');
|
|
if(user.name==dec.user&&hash==decrypt(user.hash,hash)){
|
|
|
|
const skey = new NodeRSA()
|
|
skey.importKey(keyring[req.body.sid].theirpub,'pkcs8-public')
|
|
let logkey = crypt.createHash('md5').update(crypt.randomBytes(64).toString('hex')).digest('hex')
|
|
let alias = crypt.createHash('md5').update(crypt.randomBytes(4096).toString('hex')).digest('hex')
|
|
res.send(JSON.stringify({data:skey.encrypt(JSON.stringify({login_key:logkey,alias:alias}),'base64'),enc:true,html:false,json:true,type:'key'}))
|
|
|
|
user.setDataValue('login_key',encrypt(hash,logkey))
|
|
user.setDataValue('alias',alias)
|
|
user.save()
|
|
User.sync({ alter:true })
|
|
break
|
|
}
|
|
}
|
|
})
|
|
app.use((req:any, res:any, next:any) => {
|
|
res.status(418).sendFile(__dirname+'/html/404.html')
|
|
})
|
|
|
|
//http
|
|
var httpServer = http.createServer(app);
|
|
var credentials = {key: privateKey, cert: certificate};
|
|
var httpsServer = https.createServer(credentials, app);
|
|
app.listen(8008,function local(){
|
|
log(`kanna is local http://${ip.address()}:8008`)
|
|
})
|
|
httpServer.listen(80, function http() {
|
|
log(`kanna is on http://${ip.address()} click on me click on me! :3`)
|
|
})
|
|
httpsServer.listen(443, function https() {
|
|
log(`kanna is secure now too!! https://${ip.address()}`)
|
|
})
|
|
//end
|
|
/*let l = (encrypt(JSON.stringify({
|
|
'emails':[{
|
|
'address':'grantsquires@disroot.org',
|
|
'host':'disroot.org',
|
|
'port':'993',
|
|
'creds':pass,
|
|
'salt':crypt.randomBytes(64).toString('hex')
|
|
}], //how much salt do you want? 'all of it'
|
|
'salt':[
|
|
d(),crypt.randomBytes(64).toString('hex'),crypt.randomBytes(64).toString('base64'),d()
|
|
],
|
|
'storage':'./storage/'+user.name,
|
|
|
|
}),hash))
|
|
*/
|
|
|
|
/*
|
|
todo:
|
|
*/
|