208 lines
5.1 KiB
JavaScript
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");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|