diff --git a/package.json b/package.json index fb53173c..1a2c0418 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "format": "prettier --single-quote --write 'public/*.js' 'app.js'", "test": "echo \"Error: no test specified\" && exit 1", - "start": "node app.js" + "start": "node server/portal_server.js" }, "author": "", "license": "ISC", diff --git a/public/file.js b/public/file.js new file mode 100644 index 00000000..ce680be4 --- /dev/null +++ b/public/file.js @@ -0,0 +1,10 @@ +function ProgressEmitter(name, uuid, type) { + this.name = name; + this.uuid = uuid; + this.type = type; + this.link = null; + + this.emit = () => { + + }; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index c6b31826..aa77f317 100644 --- a/public/index.html +++ b/public/index.html @@ -1,9 +1,9 @@ -Firefox Fileshare - - + Firefox Fileshare + + @@ -13,6 +13,6 @@ - + diff --git a/public/upload.js b/public/upload.js index 2d890a4f..f213490f 100644 --- a/public/upload.js +++ b/public/upload.js @@ -1,101 +1,105 @@ function onChange(event) { + var file = event.target.files[0]; var reader = new FileReader(); + reader.readAsArrayBuffer(file); + + let random_iv = window.crypto.getRandomValues(new Uint8Array(16)); + var hex = ivToStr(random_iv); + reader.onload = function(event) { let self = this; - window.crypto.subtle - .generateKey( - { - name: 'AES-CBC', - length: 128 - }, - true, - ['encrypt', 'decrypt'] - ) - .then(function(key) { - var arrayBuffer = self.result; - var array = new Uint8Array(arrayBuffer); + window.crypto.subtle.generateKey({ + name: "AES-CBC", + length: 128 + }, + true, + ["encrypt", "decrypt"]) + .then((key) => { + let arrayBuffer = self.result; + let array = new Uint8Array(arrayBuffer); - var random_iv = window.crypto.getRandomValues(new Uint8Array(16)); + window.crypto.subtle.encrypt({ + name: "AES-CBC", + iv: random_iv }, + key, + array) + .then(uploadFile.bind(null, file, hex, key)) + .catch((err) => console.error(err)); - window.crypto.subtle - .encrypt( - { - name: 'AES-CBC', - iv: random_iv - }, - key, - array - ) - .then(function(encrypted) { - var dataView = new DataView(encrypted); - var blob = new Blob([dataView], { type: file.type }); - - var fd = new FormData(); - fd.append('fname', file.name); - fd.append('data', blob, file.name); - - var xhr = new XMLHttpRequest(); - var hex = ivToStr(random_iv); - xhr.open('post', '/upload/' + hex, true); - - var li = document.createElement('li'); - var name = document.createElement('p'); - name.innerHTML = file.name; - li.appendChild(name); - - var link = document.createElement('a'); - li.appendChild(link); - - var progress = document.createElement('p'); - li.appendChild(progress); - document.getElementById('uploaded_files').appendChild(li); - - xhr.upload.addEventListener( - 'progress', - returnBindedLI(progress, name, link, li) - ); - - xhr.onreadystatechange = function() { - if (xhr.readyState == XMLHttpRequest.DONE) { - window.crypto.subtle - .exportKey('jwk', key) - .then(function(keydata) { - var curr_name = localStorage.getItem(file.name); - - localStorage.setItem(hex, xhr.responseText); - - link.innerHTML = - 'http://localhost:3000/download/' + - hex + - '/#' + - keydata.k; - link.setAttribute( - 'href', - 'http://localhost:3000/download/' + hex + '/#' + keydata.k - ); - - console.log( - 'Share this link with a friend: http://localhost:3000/download/' + - hex + - '/#' + - keydata.k - ); - }); - } - }; - - xhr.send(fd); - }) - .catch(function(err) { - console.error(err); - }); - }) - .catch(function(err) { - console.error(err); - }); + }).catch((err) => console.error(err)); }; - reader.readAsArrayBuffer(file); +} + +let uploadFile = (file, hex, key, encrypted) => { + var dataView = new DataView(encrypted); + var blob = new Blob([dataView], { type: file.type }); + + var fd = new FormData(); + fd.append("fname", file.name); + fd.append("data", blob, file.name); + + var xhr = new XMLHttpRequest(); + + xhr.open("post", "/upload/" + hex, true); + + let prog = new window.ProgressEmitter(file.name, hex, file.type); + + var li = document.createElement("li"); + var name = document.createElement("p"); + name.innerHTML = file.name; + li.appendChild(name); + + var link = document.createElement("a"); + li.appendChild(link); + + var progress = document.createElement("p"); + li.appendChild(progress); + + + document.getElementById("uploaded_files").appendChild(li); + + + xhr.upload.addEventListener("progress", returnBindedLI(progress, name, link, li)); + + xhr.onreadystatechange = function() { + if (xhr.readyState == XMLHttpRequest.DONE) { + window.crypto.subtle.exportKey("jwk", key).then(function(keydata) { + var curr_name = localStorage.getItem(file.name); + + localStorage.setItem(hex, xhr.responseText); + + prog.link = "http://localhost:3000/download/" + hex + "/#" + keydata.k; + prog.emit(); + + link.innerHTML = "http://localhost:3000/download/" + hex + "/#" + keydata.k; + link.setAttribute("href", "http://localhost:3000/download/" + hex + "/#" + keydata.k); + + console.log("Share this link with a friend: http://localhost:3000/download/" + hex + "/#" + keydata.k); + }) + } + }; + + xhr.send(fd); + // setupLI(file.name); + +} + +function setupLI(emitter) { + var li = document.createElement("li"); + var name = document.createElement("p"); + name.innerHTML = emitter; + li.appendChild(name); + + var link = document.createElement("a"); + link.addEventListener('NotifyProgress', (progress_event) => { + console.log(progress_event); + }); + li.appendChild(link); + + var progress = document.createElement("p"); + li.appendChild(progress); + document.getElementById("uploaded_files").appendChild(li); } function ivToStr(iv) { diff --git a/server/portal_server.js b/server/portal_server.js new file mode 100644 index 00000000..4cc91688 --- /dev/null +++ b/server/portal_server.js @@ -0,0 +1,116 @@ + +const express = require("express") +const busboy = require("connect-busboy"); +const path = require("path"); +const fs = require("fs-extra"); +const bodyParser = require("body-parser"); +const crypto = require("crypto"); + +const app = express() +const redis = require("redis"), + client = redis.createClient(); + +client.on("error", function(err) { + console.log(err); +}) + +app.use(busboy()); +app.use(bodyParser.json()); +app.use(express.static(path.join(__dirname, "../public"))); + +app.get("/download/:id", function(req, res) { + res.sendFile(path.join(__dirname + "/../public/download.html")); +}); + +app.get("/assets/download/:id", function(req, res) { + + let id = req.params.id; + if (!validateID(id)){ + res.send(404); + return; + } + + + client.hget(id, "filename", function(err, reply) { // maybe some expiration logic too + if (!reply) { + res.sendStatus(404); + } else { + res.setHeader("Content-Disposition", "attachment; filename=" + reply); + res.setHeader("Content-Type", "application/octet-stream"); + + res.download(__dirname + "/../static/" + id, reply, function(err) { + if (!err) { + client.del(id); + fs.unlinkSync(__dirname + "/../static/" + id); + } + }); + } + }) + +}); + +app.post("/delete/:id", function(req, res) { + let id = req.params.id; + + if (!validateID(id)){ + res.send(404); + return; + } + + let delete_token = req.body.delete_token; + + if (!delete_token){ + res.sendStatus(404); + } + + client.hget(id, "delete", function(err, reply) { + if (!reply) { + res.sendStatus(404); + } else { + client.del(id); + fs.unlinkSync(__dirname + "/../static/" + id); + res.sendStatus(200); + } + }) +}); + +app.post("/upload/:id", function (req, res, next) { + + if (!validateID(req.params.id)){ + res.send(404); + return; + } + + var fstream; + req.pipe(req.busboy); + req.busboy.on("file", function (fieldname, file, filename) { + console.log("Uploading: " + filename); + + //Path where image will be uploaded + fstream = fs.createWriteStream(__dirname + "/../static/" + req.params.id); + file.pipe(fstream); + fstream.on("close", function () { + let id = req.params.id; + let uuid = crypto.randomBytes(10).toString('hex'); + + client.hmset([id, "filename", filename, "delete", uuid]); + + // delete the file off the server in 24 hours + // setTimeout(function() { + // fs.unlinkSync(__dirname + "/static/" + id); + // }, 86400000); + + client.expire(id, 86400000); + console.log("Upload Finished of " + filename); + res.send(uuid); + }); + }); +}); + +app.listen(3000, function () { + console.log("Portal app listening on port 3000!") +}) + +function validateID(route_id) { + return route_id.match(/^[0-9a-fA-F]{32}$/) !== null; +} \ No newline at end of file