commit fdc38e3538cf8fe30834b4e0d08fc597e00088c9 Author: Dorian Niemiec Date: Fri Aug 11 03:23:27 2023 +0200 Initial commit diff --git a/index.js b/index.js new file mode 100644 index 0000000..c07772c --- /dev/null +++ b/index.js @@ -0,0 +1,278 @@ +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 net = require("net"); +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 +var scgiConf = {}; +try { + scgiConf = JSON.parse(fs.readFileSync(__dirname + "/../../../orangecircle-config.json")); +} catch (ex) { + // Use defaults +} + +// Load default configuration +if (scgiConf.path === undefined) scgiConf.path = "/scgi"; +scgiConf.path = scgiConf.path.replace(/([^\/])\/+$/, "$1"); +if (scgiConf.host === undefined) scgiConf.host = "localhost"; +if (scgiConf.port === undefined) scgiConf.port = 4000; + +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 executeSCGI(req, res, dh, nEnv) { + // Function to execute SCGI scripts + var env = JSON.parse(JSON.stringify(process.env)); + var nEnvKeys = Object.keys(nEnv); + for (var i = 0; i < nEnvKeys.length; i++) { + env[nEnvKeys[i]] = nEnv[nEnvKeys[i]]; + } + var buffer = ""; + var headerendline = -1; + var cned = false; + var dataHandler = function (data) { + buffer += data.toString("latin1"); + var m = null; + if (!cned) m = buffer.match(/(?:\r\n\r\n|\n\n|\r\r)/); + if (!cned && m) { + cned = true; + eol = m[0]; + headerendline = m.index; + var bheaders = buffer.substr(0, headerendline).split(/(?:\r\n|\n|\r)/); + var bheaderso = {}; + if (dh) bheaderso = dh; + var code = 200; + var msg = "OK"; + if (bheaders[0].indexOf("HTTP/") == 0) { + var heada = bheaders.shift(); + var hso = heada.split(" "); + code = hso[1]; + if (hso[2] !== undefined) msg = heada.split(" ").splice(2).join(" "); + } else if (bheaders[0].indexOf(":") == -1) { + var heada = bheaders.shift(); + var hso = heada.split(" "); + if (hso[0].match(/^[0-9]{3}$/)) { + code = hso[0]; + if (hso[1] !== undefined) msg = heada.split(" ").splice(1).join(" "); + } + } + for (var i = 0; i < bheaders.length; i++) { + var headerp = bheaders[i].split(": "); + var headern = headerp.shift(); + var headerv = headerp.join(": "); + if (headern.toLowerCase() == "status") { + code = headerv.split(" ")[0]; + if (headerv.split(" ")[1] !== undefined) msg = headerv.split(" ").splice(1).join(" "); + } else if (headern.toLowerCase() == "set-cookie") { + if (!bheaderso["Set-Cookie"]) bheaderso["Set-Cookie"] = []; + bheaderso["Set-Cookie"].push(headerv); + } else { + bheaderso[headern] = headerv; + } + } + + if (code == 200 && (bheaderso["Location"] || bheaderso["location"])) { + code = 302; + msg = "Found"; + } + + try { + res.writeHead(code, msg, bheaderso); + res.write(buffer.substr(headerendline + eol.length), "latin1"); + } catch (ex) { + if (!callServerError) { + res.writeHead(500); + res.end(ex.stack); + } else { + callServerError(500, "OrangeCircle/" + version, ex); + } + return; + } + } else { + if (cned && !res.finished) res.write(data); + } + }; + + var socket = net.createConnection({ + host: scgiConf.host, + port: scgiConf.port, + }, function () { + socket.on("data", dataHandler); + socket.on("end", function () { + res.end(); + }); + var envEncoded = ""; + var envNames = Object.keys(env); + for (var i = 0; i < envNames.length; i++) { + var envName = String(envNames[i]).replace(/\0/g, ""); + var envValue = String(env[envNames[i]]).replace(/\0/g, ""); + if(envName == "CONTENT_LENGTH") { + // CONTENT_LENGTH environment variable must be first!!! + envEncoded = envName + "\0" + envValue + "\0" + envEncoded; + } else { + envEncoded += envName + "\0" + envValue + "\0"; + } + } + socket.write(envEncoded.length + ":" + envEncoded + ","); + req.pipe(socket, {end: false}); + }).on("error", function (error) { + if (!callServerError) { + res.writeHead(500, { + "Content-Type": "text/html", + "Server": "OrangeCircle/" + version + }); + res.end("

OrangeCircle Error!

Reason: " + error.message + "

"); + } else { + callServerError(500, "OrangeCircle/" + version, error); + } + }); + + } + + function executeSCGIWithEnv(a, req, res, pubip, port, software, dh) { + // Function to set up environment variables and execute sCGI scripts + var b = href.replace(scgiConf.path,"") + var nEnv = {}; + if (req.headers.authorization) { + nEnv["AUTH_TYPE"] = req.headers.authorization.split(" ")[0]; + if (nEnv["AUTH_TYPE"] == "Basic") { + var remoteCred = req.headers.authorization.split(" ")[1]; + if (!remoteCred) { + nEnv["REMOTE_USER"] = "orangecircle_cgi_invalid_user"; + } else { + var remoteCredDecoded = Buffer.from(remoteCred, "base64").toString("utf8"); + nEnv["REMOTE_USER"] = remoteCredDecoded.split(":")[0]; + } + } else { + nEnv["REMOTE_USER"] = "svrjs_this_property_is_not_yet_supported_by_orangecircle_cgi"; + } + } + nEnv["QUERY_STRING"] = req.url.split("?")[1]; + if (nEnv["QUERY_STRING"] == undefined || nEnv["QUERY_STRING"] == "undefined") nEnv["QUERY_STRING"] = ""; + nEnv["SERVER_SOFTWARE"] = software; + nEnv["SERVER_PROTOCOL"] = "HTTP/" + req.httpVersion; + if (pubip && (port !== null && port !== undefined)) { + nEnv["SERVER_PORT"] = port; + nEnv["SERVER_ADDR"] = pubip.replace(/^::ffff:/i, ""); + if (nEnv["SERVER_ADDR"].indexOf(":") != -1) nEnv["SERVER_ADDR"] = "[" + nEnv["SERVER_ADDR"] + "]"; + } + nEnv["SERVER_NAME"] = req.headers.host; + nEnv["DOCUMENT_ROOT"] = process.cwd(); + nEnv["SCRIPT_NAME"] = scgiConf.path; + nEnv["PATH_INFO"] = decodeURI(b); + nEnv["PATH_TRANSLATED"] = b ? decodeURI((process.cwd() + (require("os").platform == "win32" ? b.replace(/\//g, "\\") : b)).replace((require("os").platform == "win32" ? /\\\\/g : /\/\//g), (require("os").platform == "win32" ? "\\" : "/"))) : ""; + nEnv["REQUEST_METHOD"] = req.method; + nEnv["SCGI"] = "1"; + nEnv["REQUEST_URI"] = (!origHref || origHref == href) ? req.url : (origHref + (uobject.search ? ("?" + uobject.search) : "")); + nEnv["REMOTE_ADDR"] = (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, ""); + nEnv["REMOTE_PORT"] = (req.socket.realRemotePort ? req.socket.realRemotePort : req.socket.remotePort); + if (req.headers["content-type"]) nEnv["CONTENT_TYPE"] = req.headers["content-type"]; + nEnv["CONTENT_LENGTH"] = "0"; + if (req.headers["content-length"]) nEnv["CONTENT_LENGTH"] = req.headers["content-length"]; + var nh = JSON.parse(JSON.stringify(req.headers)); + delete nh["content-type"]; + delete nh["content-length"]; + var nhKeys = Object.keys(nh); + for (var i = 0; i < nhKeys.length; i++) { + nEnv["HTTP_" + nhKeys[i].replace(/[^0-9A-Za-z]+/g, "_").toUpperCase()] = req.headers[nhKeys[i]]; + } + executeSCGI(req, res, dh, nEnv); + } + + if (href == scgiConf.path || href.indexOf(scgiConf.path + "/") == 0) { + try { + executeSCGIWithEnv( + href, + req, + res, + req.socket.localAddress, + req.socket.localPort, + getCustomHeaders ? + getCustomHeaders()["Server"] + + " OrangeCircle/" + + version : + "SVR.JS/" + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") OrangeCircle/" + + version, + bheaders + ); + } catch (ex) { + if (!callServerError) { + res.writeHead(500, "Internal Server Error", abheaders); + res.write( + "500 Internal Server Error

500 Internal Server Error

A server had unexpected exception. Below, the stack trace of the error is shown:

" + + ex.stack.replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ /g, " ") + + "

Please contact the developer/administrator of the website.

SVR.JS " + + configJSON.version + + " (" + + os.platform()[0].toUpperCase() + + os.platform().slice(1) + + "; Node.JS/" + + process.version + + ") OrangeCircle/" + + version + + " " + + (req.headers.host == undefined ? "" : " on " + req.headers.host) + + "

" + ); + res.end(); + } else { + callServerError(500, "OrangeCircle/" + version, ex); + } + } + } else if (href == "/orangecircle-config.json" && path.normalize(__dirname + "/../../..") == process.cwd()) { + if (!callServerError) { + res.writeHead(200, "OK", { + "Content-Type": "application/json", + "Server": "OrangeCircle/" + version + }); + res.end(JSON.stringify(exttointerpreteruser, null, 2)); + } else { + callServerError(200, "OrangeCircle/" + version, exttointerpreteruser); + } + } else { + elseCallback(); + } + } +} + +module.exports = Mod; diff --git a/mod.info b/mod.info new file mode 100755 index 0000000..273b5a3 --- /dev/null +++ b/mod.info @@ -0,0 +1,4 @@ +{ + "name": "DorianTech OrangeCircle SCGI client for SVR.JS", + "version": "1.0.0" +}