yellowsquare/index.js
2023-08-11 19:53:57 +02:00

364 lines
14 KiB
JavaScript

var https = require("https");
var os = require("os");
var http = require("http");
var url = require("url");
var fs = require("fs");
var path = require("path");
var stream = require("stream");
var version = "UNKNOWN";
try {
version = JSON.parse(fs.readFileSync(__dirname + "/mod.info")).version;
} catch (ex) {
// Can't determine version
}
var configJSONS = JSON.parse(fs.readFileSync("config.json")); // Read configuration JSON
class RequestBodyStream extends stream.Readable {
constructor(sourceStream) {
super();
this.sourceStream = sourceStream;
// When the source stream emits data, push it to the wrapper stream
this.sourceStream.on('data', (chunk) => {
this.push(chunk);
});
// When the source stream ends, push null to indicate the end of the wrapper stream
this.sourceStream.on('end', () => {
this.push(null);
});
}
// Implement the _read method to handle the read operation
_read() {}
}
class ErrorStream extends stream.Writable {
constructor() {
super();
this.buffer = Buffer.alloc(0);
}
_write(chunk, encoding, callback) {
// Concatenate the incoming chunk to the buffer
this.buffer = Buffer.concat([this.buffer, chunk]);
// Call the callback to indicate that the write operation is complete
callback();
}
}
function Mod() {}
Mod.prototype.callback = function (req, res, serverconsole, responseEnd, href, ext, uobject, search, defaultpage, users, page404, head, foot, fd, elseCallback, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData) {
return function () {
if (!configJSON) {
configJSON = configJSONS;
}
if (!getCustomHeaders) {
var bheaders = JSON.parse(JSON.stringify(configJSON.customHeaders));
} else {
var bheaders = getCustomHeaders();
}
bheaders["Content-Type"] = "text/html"; // HTML output
if (!getCustomHeaders) {
bheaders["Server"] =
"SVR.JS/" +
configJSON.version +
" (" +
os.platform()[0].toUpperCase() +
os.platform().slice(1) +
")"; // Add Server header
}
var abheaders = JSON.parse(JSON.stringify(bheaders));
function executeJSGI(fname, req, res, dh, jsgiRequestObject) {
// Function to execute JSGI scripts
if (!fname.match(/\.jsgi(?:\.js)?$/)) {
elseCallback();
return;
}
try {
var JSGIApp = require(process.cwd() + "/" + fname);
var jsgiResponseObject = {};
if (typeof JSGIApp === "object" && JSGIApp.app) {
jsgiResponseObject = JSGIApp.app(jsgiRequestObject);
} else {
throw new Error("JSGI app must have app key in exports objects!!!");
}
if (jsgiRequestObject.jsgi.errors.readable) jsgiRequestObject.jsgi.errors.end();
var errors = jsgiRequestObject.jsgi.errors.buffer.toString();
if (errors.trim().length > 0) {
serverconsole.errmessage("There were JSGI application errors:");
serverconsole.errmessage(errors);
}
if (!jsgiResponseObject.status) jsgiResponseObject.status = 200;
if (!getCustomHeaders) {
var aheaders = JSON.parse(JSON.stringify(configJSON.customHeaders));
} else {
var aheaders = getCustomHeaders();
}
if (jsgiResponseObject.headers) {
var hKeys = Object.keys(jsgiResponseObject.headers);
for (var i = 0; i < hKeys.length; i++) {
aheaders[hKeys[i]] = jsgiResponseObject.headers[hKeys[i]];
}
}
if (!jsgiResponseObject.body) jsgiResponseObject.body = [""];
if (typeof jsgiResponseObject.body === "string") {
res.writeHead(jsgiResponseObject.status, http.STATUS_CODES[jsgiResponseObject.status], aheaders);
res.write(jsgiResponseObject.body);
res.end();
} else if (typeof jsgiResponseObject.body.forEach !== "function") {
throw new Error("JSGI app must return body, which has forEach function.");
} else {
res.writeHead(jsgiResponseObject.status, http.STATUS_CODES[jsgiResponseObject.status], aheaders);
jsgiResponseObject.body.forEach(function (chunk) {
res.write(chunk);
});
res.end();
}
} catch (error) {
if (!callServerError) {
res.writeHead(500, {
"Content-Type": "text/html",
"Server": "YellowSquare/" + version
});
res.end("<html><head></head><body><h1>YellowSquare Error!</h1><p>Reason: " + error.message + "</p></body></html>");
} else {
callServerError(500, "YellowSquare/" + version, error);
}
}
}
function executeJSGIWithReqObj(a, b, req, res, pubip, port, software, dh) {
// Function to set up request object and execute JSGI scripts
var inputStream = new RequestBodyStream(req);
var errorStream = new ErrorStream();
var jsgiRequestObject = {
version: req.httpVersion.split("."),
method: req.method,
headers: req.headers,
input: inputStream,
scriptName: a,
pathInfo: decodeURI(b),
pathTranslated: b ? decodeURI((process.cwd() + (require("os").platform == "win32" ? b.replace(/\//g, "\\") : b)).replace((require("os").platform == "win32" ? /\\\\/g : /\/\//g), (require("os").platform == "win32" ? "\\" : "/"))) : "",
scheme: req.socket.encrypted ? "https" : "http",
env: {},
jsgi: {
version: [0, 3],
errors: errorStream,
multithread: false,
multiprocess: true,
runOnce: false,
async: false,
cgi: false,
ext: {}
},
serverSoftware: software,
remoteAddr: (req.socket.realRemoteAddress ? req.socket.realRemoteAddress : ((req.headers["x-forwarded-for"] && configJSON.enableIPSpoofing) ? req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, "") : req.socket.remoteAddress)).replace(/^::ffff:/i, ""),
remotePost: (req.socket.realRemotePort ? req.socket.realRemotePort : req.socket.remotePort)
};
if (req.headers.authorization) {
jsgiRequestObject.authType = req.headers.authorization.split(" ")[0];
if (jsgiRequestObject.authType == "Basic") {
var remoteCred = req.headers.authorization.split(" ")[1];
if (!remoteCred) {
jsgiRequestObject.remoteUser = "yellowsquare_jsgi_invalid_user";
} else {
var remoteCredDecoded = Buffer.from(remoteCred, "base64").toString("utf8");
jsgiRequestObject.remoteUser = remoteCredDecoded.split(":")[0];
}
} else {
jsgiRequestObject.remoteUser = "svrjs_this_property_is_not_yet_supported_by_yellowsquare_jsgi";
}
}
jsgiRequestObject.queryString = req.url.split("?")[1];
if (jsgiRequestObject.queryString == undefined || jsgiRequestObject.queryString == "undefined") jsgiRequestObject.queryString = "";
if (pubip && (port !== null && port !== undefined)) {
jsgiRequestObject.port = port;
jsgiRequestObject.host = pubip.replace(/^::ffff:/i, "");
if (jsgiRequestObject.host.indexOf(":") != -1) jsgiRequestObject.host = "[" + jsgiRequestObject.host + "]";
}
executeJSGI("." + a, req, res, dh, jsgiRequestObject);
}
if (href.indexOf("/jsgi-bin") == 0) {
fs.stat("." + href, function (err, stats) {
if (!err) {
if (!stats.isFile()) {
elseCallback();
} else {
try {
executeJSGIWithReqObj(
href,
"",
req,
res,
req.socket.localAddress,
req.socket.localPort,
getCustomHeaders ?
getCustomHeaders()["Server"] +
" YellowSquare/" +
version :
"SVR.JS/" +
configJSON.version +
" (" +
os.platform()[0].toUpperCase() +
os.platform().slice(1) +
"; Node.JS/" +
process.version +
") YellowSquare/" +
version,
bheaders
);
} catch (ex) {
if (!callServerError) {
res.writeHead(500, "Internal Server Error", abheaders);
res.write(
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex.stack.replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ /g, "&nbsp;") +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON.version +
" (" +
os.platform()[0].toUpperCase() +
os.platform().slice(1) +
"; Node.JS/" +
process.version +
") YellowSquare/" +
version +
" " +
(req.headers.host == undefined ? "" : " on " + req.headers.host) +
"</p></body></html>"
);
res.end();
} else {
callServerError(500, "YellowSquare/" + version, ex);
}
}
}
} else if (err && err.code == "ENOTDIR") {
function checkPath(pth, cb, a) {
// Function to check the path of the file and execute CGI script
var cpth = pth.split("/");
if (cpth.length < 3) {
cb(false);
return;
}
if (!a) b = [];
else var b = a.split("/");
var isFile = false;
fs.stat(pth, function (err, stats) {
if (!err && stats.isFile()) {
cb({
fpth: pth,
rpth: (a !== undefined ? "/" + a : "")
})
} else {
b.unshift(cpth.pop());
return checkPath(cpth.join("/"), cb, b.join("/"));
}
});
}
checkPath("." + href, function (pathp) {
if (!pathp) {
elseCallback();
} else {
try {
executeJSGIWithReqObj(
pathp.fpth.substr(1),
pathp.rpth,
req,
res,
req.socket.localAddress,
req.socket.localPort,
getCustomHeaders ?
getCustomHeaders()["Server"] +
" YellowSquare/" +
version :
"SVR.JS/" +
configJSON.version +
" (" +
os.platform()[0].toUpperCase() +
os.platform().slice(1) +
"; Node.JS/" +
process.version +
") YellowSquare/" +
version,
bheaders
);
} catch (ex) {
if (!callServerError) {
res.writeHead(500, "Internal Server Error", abheaders);
res.write(
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
ex.stack.replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ /g, "&nbsp;") +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON.version +
" (" +
os.platform()[0].toUpperCase() +
os.platform().slice(1) +
"; Node.JS/" +
process.version +
") YellowSquare/" +
version +
" " +
(req.headers.host == undefined ? "" : " on " + req.headers.host) +
"</p></body></html>"
);
res.end();
} else {
callServerError(500, "YellowSquare/" + version, ex);
}
}
}
});
} else if (err && err.code == "ENOENT") {
elseCallback(); //Invoke default error handler
} else {
if (!callServerError) {
res.writeHead(500, "Internal Server Error", abheaders);
res.write(
"<html><head><title>500 Internal Server Error</title></head><body><h1>500 Internal Server Error</h1><p>A server had unexpected exception. Below, the stack trace of the error is shown:</p><code>" +
err.stack.replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ /g, "&nbsp;") +
"</code><p>Please contact the developer/administrator of the website.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS " +
configJSON.version +
" (" +
os.platform()[0].toUpperCase() +
os.platform().slice(1) +
"; Node.JS/" +
process.version +
") YellowSquare/" +
version +
" " +
(req.headers.host == undefined ? "" : " on " + req.headers.host) +
"</p></body></html>"
);
res.end();
} else {
callServerError(500, "YellowSquare/" + version, err);
}
}
});
} else if (href == "/YellowSquare-interpreters.json" && path.normalize(__dirname + "/../../..") == process.cwd()) {
if (!callServerError) {
res.writeHead(200, "OK", {
"Content-Type": "application/json",
"Server": "YellowSquare/" + version
});
res.end(JSON.stringify(exttointerpreteruser, null, 2));
} else {
callServerError(200, "YellowSquare/" + version, exttointerpreteruser);
}
} else {
elseCallback();
}
}
}
module.exports = Mod;