2023-08-11 19:53:57 +02:00
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
}
2023-09-15 08:51:04 +02:00
var configJSONS = JSON . parse ( fs . readFileSync ( _ _dirname + "/../../../config.json" ) ) ; // Read configuration JSON
2023-08-11 19:53:57 +02:00
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 ,
2023-09-03 11:26:34 +02:00
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 , "" )
2023-08-11 19:53:57 +02:00
} ;
2023-09-03 11:26:34 +02:00
if ( req . socket . realRemoteAddress && req . socket . realRemotePort ) {
jsgiRequestObject . remotePort = req . socket . realRemotePort ;
} else if ( ! ( req . socket . realRemoteAddress && ! req . socket . realRemotePort ) ) {
jsgiRequestObject . remotePort = req . socket . remotePort ;
}
2023-08-11 19:53:57 +02:00
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 ) ;
}
2023-08-14 18:13:56 +02:00
if ( href . match ( new RegExp ( "/jsgi-bin(?:$|[?#/])" , os . platform ( ) == "win32" ? "i" : "" ) ) ) {
2023-08-11 19:53:57 +02:00
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 , " " ) +
"</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 , " " ) +
"</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 , " " ) +
"</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 {
elseCallback ( ) ;
}
}
}
module . exports = Mod ;