/**
Post-bootstrap initialization code for s2's fslcgi. Must live in the
root of "resource directory", but that (with sufficient hacking) may
live outside of the web-root.
*/
affirm 'undefined' !== typename $CGI /* expecting CGI module to be loaded under this name */;
affirm Fossil.require;
$CGI.config = {
resourceDir: Fossil.file.canonicalName(Fossil.file.dirPart(__FILE, true))
};
scope {
const C = $CGI;
var uri = s2.getenv('SCRIPT_NAME');
// Set up cgiRoot (the "standard" way of getting the root dir
// for link-building purposes). This kludgery is not generic...
if(uri) { C.config.cgiRoot = uri + '/' }
else{
uri = s2.getenv('REQUEST_URI');
C.request.ENV || (C.request.REQUEST_URI = uri);
if(uri){
var adj = uri.split('?').0.split('fslcgi',2);
C.config.cgiRoot = adj.0.concat('fslcgi', '/');
}
}
C.config.localServerMode =
(var serverName = s2.getenv('SERVER_NAME'))
&& (0<serverName.indexOf(".local"));
;;
}
/**
A convenience alias for s2.io.output (or functionally
equivalent).
*/
$CGI.out = s2.io.output;
/**
Sends its RHS argument output to the (buffered) response body.
Returns this object (so it can be chained).
*/
$CGI.'operator<<' = proc(self,arg){
return s2.io.output(arg), this;
};
/**
Removes "potentially security-relevant" properties from the
exception ex. Returns ex.
*/
$CGI.scrubException = proc(ex){
if(this.config.scrubExceptions){
unset ex.script, ex.stackTrace, ex.line, ex.column // security-relevant
}
return ex;
};
$CGI.util = {
/**
Converts all key/value pairs from obj to a string of urlencoded
key value pairs, separated by '&'. If addQMark is true then the
result is prefixed by a '?', otherwise it is not.
If obj has no properties then an empty string is returned,
regardless of addQMark.
*/
objToUrlOpt: proc(obj, addQMark = false){
affirm obj && obj.eachProperty /* expecting an Object */;
buf.reset();
obj.eachProperty(each);
return buf.length()
? addQMark
? '?' + buf.toString()
: buf.toString()
: '';
}.importSymbols({
buf: s2.Buffer.new(),
each: proc(k,v){
buf.length() && buf.append('&');
buf.append($CGI.urlencode(''+k),'=',$CGI.urlencode(''+v));
/* Reminder to self: $CGI.urlencode() and friends require
that $CGI be their 'this' because they reuse an internal
buffer on each call to save on allocations.
*/
}
}),
absoluteLink: proc(path,label=path, urlOpt){
affirm 'string' === typename path;
const hasQ = path.indexOf('?')>=0;
var optStr = this.objToUrlOpt(urlOpt, !hasQ );
return fmt.applyFormat(
$CGI.config.cgiRoot,
path+(optStr ? hasQ ? '&' + optStr : optStr : ''),
label);
}.importSymbols({
fmt: "<a href='%1$s%2$s'>%3$s</a>"
});
};
$CGI.resourcePath = proc(name){
return this.config.resourceDir.concat(name);
};
$CGI.getCLIFlag = proc(k, default){
return (s2.ARGV && s2.ARGV.flags)
? s2.ARGV.flags.hasOwnProperty(k) ? s2.ARGV.flags[k] : default
: default;
};
$CGI.getFossilInstance = proc(){
//throw "Don't call this - use require() instead.";
var f = Fossil.require.fsl /* internal details */;
affirm f inherits Fossil.Context;
if(!f.db){
var repoDb = this.getCLIFlag('repo-db');
repoDb || throw "Pass --repo-db=/path/to/repo.db in the "
+ "SCRIPT flags (after --) to the main CGI script.";
f = Fossil.Context.new().openRepo(repoDb);
//print("Opened: ",callee.f.db.filename);
}
return f;
};
// incomplete... not yet sure what i want.
$CGI.request.pathInfoList = scope {
var ps, p = s2.getenv('PATH_INFO');
if(p && '/'!==p){
ps = p.split('/');
// Trim leading/trailing null entries caused
// by leading/trailing slashes:
ps.0 || ps.shift();
ps[ps.length()-1] || ps.pop();
}
ps;
};
if(1){ // only for testing
print($CGI.request.toJSONString(2));
}else if(0){
const c = $CGI;
c.setContentType('text/plain');
print("$CGI sanity checks...");
c << 'Fossil.time.runTimeMs() says: '
<< Fossil.time.runTimeMs()
<< '\nCLI flags:\n'
<< (s2.ARGV ? s2.ARGV.toJSONString(2) : 'none')
<< '\n'
;
const f = c.getFossilInstance();
f.db || f.openCheckout();
c << 'Fossil context: ' << f << ' repo db: ' << f.db.filename << '\n';
c << "pathInfoList = "<<c.request.pathInfoList<<'\n';
c << "resourcePath(foo/bar.baz)==>"
<< c.resourcePath('foo/bar.baz')
<< '\n';
c << "Most recent timeline event: "
<< f.db.selectRow('SELECT * FROM event ORDER BY mtime DESC')
.toJSONString(2)
<< '\n';
if(0){
// Workaround: c is a Native, and those are rejected
// by the C-level toJSON bits:
var obj = {};
c.eachProperty(proc(k,v){obj[k]=v});
c << '$CGI properties:\n' << obj.toJSONString(4) << '\n';
}
if(0){
c << 'objToUrlOpt: '
<< c.util.objToUrlOpt({a:3,c:'a b'})
<< '\n';
c << 'objToUrlOpt again (should be empty): '
<< c.util.objToUrlOpt({},true)
<< '\n';
c << 'absoluteLink: '
<< c.util.absoluteLink('foo/bar/baz?x=y', 'link', {a:1,b:'hi !'})
<< '\n';
c << 'absoluteLink again: '
<< c.util.absoluteLink('foo/bar/', 'link', {a:1,b:'hi !'})
<< '\n';
c << 'Fossil.artifactTypes = '<<Fossil.artifactTypes<<'\n';
}
c << '\nThe end. Fossil.time.runTimeMs() says: '
<< Fossil.time.runTimeMs()
<< '\n';
//throw "testing discarding of buffered output";
}