Fawkes/main.js

208 lines
5.1 KiB
JavaScript

/*
* Copyright (C) 2021 Thomas Van Acker
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*
* This project is hosted on https://git.bitscuit.be/bitscuit/Fawkes
*
*/
// Imports
const http = require("http");
const url = require("url");
const fs = require("fs");
const util = require("util");
const crypto = require("crypto");
// Vars
const hostname = "192.168.0.128"; // Enter your local IP address here
const port = 8001; // Enter a port number here
var files = {};
// Create HTTP server
const server = http.createServer((req, res) => {
// Parse url
const requrl = url.parse(req.url);
var reqpath = requrl.pathname;
if(reqpath == "/") reqpath = "/index.html";
// Respond to requests
try{
// Return requested page
res.statusCode = 200;
res.write(fs.readFileSync("html"+reqpath));
res.end();
}catch(error){
// 404 if page not found
res.statusCode = 404;
res.setHeader("Content-Type", "text/plain");
res.end("404 - Page Not Found");
}
});
// Create socket.io server
const io = require("socket.io")(server); // Can only be done when server is created
io.on("connection", (socket) => {
console.log("New user connected!");
// Give user unique ID
var userId = sha256("USER"+rand(0,9999999)+"TIME"+Date.now()).substr(0, 10);
console.log("userId = "+userId);
// Init directory structure
fs.mkdirSync("fawkes/files/"+userId);
// Init socket listeners
// Disconnect
socket.on("disconnect", () => {
console.log(userId+": disconnect");
// Remove all files from this user
fs.rmSync("fawkes/files/"+userId, {recursive: true});
});
// File upload
socket.on("FILE_START", (data) => {
console.log(userId+": file upload ("+data.name+", "+data.length+" bytes)");
// Check length
if(data.length > 20000000){
console.log(userId+": file too large (data.length = "+data.length+")");
socket.emit("FILE_ERROR", {name:data.name, error:"File too large. Max size = 20MB"});
return;
}
// Check if exists
if((userId+data.name) in files){
console.log(userId+": file already exists (data.name = "+data.name+")");
socket.emit("FILE_ERROR", {name:data.name, error:"File had already been uploaded."});
return;
}
// Create file
var name = data.name;
var path = "fawkes/files/"+userId+"/"+name;
var file = {
length: data.length,
name: data.name,
path: path,
numSegments: data.length/100000 + 1,
nextSegment: 0
};
files[userId+data.name] = file;
// Ask client to begin upload
socket.emit("FILE_ACK", {name:file.name, nextSegment:file.nextSegment, numSegments:file.numSegments});
});
// File contents upload
socket.on("FILE_DATA", (data) => {
console.log(userId+": file data ("+data.name+", segment "+data.segment+", "+data.data.length+" bytes)");
// Check if file exists
if(!((userId+data.name) in files)){
console.log(userId+": received segment for unknown file (data.name = "+data.name+")");
socket.emit("FILE_ERROR", {name:data.name, error:"Received segment for unknown file."});
return;
}
// Get file
var file = files[userId+data.name];
// Check size
if(data.data.length > 100000){
console.log(userId+": segment too large (data.name = "+data.name+", data.data.length = "+data.data.length+")");
socket.emit("FILE_ERROR", {name:data.name, error:"Segment too large."});
return;
}
// Check segment number
if(data.segment != file.nextSegment || data.segment >= file.numSegments){
console.log(userId+": unexpected segment (data.name = "+data.name+", data.segment = "+data.segment+")");
socket.emit("FILE_ERROR", {name:data.name, error:"Unexpected segment."});
return;
}
// Write data
fs.appendFile(file.path, data.data, 'Binary', function(err){
if(err){
// Error
console.log(userId+": couldn't write to file (error = "+err+")");
socket.emit("FILE_ERROR", {name:data.name, error:"Internal server error. Couldn't write to file."});
}else{
file.nextSegment++;
if(file.nextSegment < file.numSegments-1){
// Ask for new segment
socket.emit("FILE_ACK", {name:file.name, nextSegment:file.nextSegment, numSegments:file.numSegments});
}else{
// When file fully uploaded
socket.emit("FILE_DONE", {name:file.name});
}
}
});
});
});
// Start HTTP server
server.listen(port, hostname, () => {
console.log("Server running at http://"+hostname+":"+port);
});
// Helper methods
function rand(min, max){
return Math.floor(Math.random()*(max-min) + min);
}
function sha256(message){
return crypto.createHash("sha256").update(message).digest("hex");
}