Gopher server revamp!

* gophermap support
* More generic and flexible server
This commit is contained in:
Bryan Ashby 2020-11-27 00:54:56 -07:00
parent 228cd79989
commit f7e4b57763
No known key found for this signature in database
GPG Key ID: B49EB437951D2542
8 changed files with 98 additions and 29 deletions

View File

@ -9,6 +9,7 @@ This document attempts to track **major** changes and additions in ENiGMA½. For
* New `PV` ACS check for arbitrary user properties. See [ACS](./docs/configuration/acs.md) for details. * New `PV` ACS check for arbitrary user properties. See [ACS](./docs/configuration/acs.md) for details.
* The `message` arg used by `msg_list` has been deprecated. Please starting using `messageIndex` for this purpose. Support for `message` will be removed in the future. * The `message` arg used by `msg_list` has been deprecated. Please starting using `messageIndex` for this purpose. Support for `message` will be removed in the future.
* Added ability to export/download messages. This is enabled in the default menu. See `messageAreaViewPost` in [the default message base template](./misc/menu_templates/message_base.in.hjson) and look for the download options (`@method:addToDownloadQueue`, etc.) for details on adding to your system! * Added ability to export/download messages. This is enabled in the default menu. See `messageAreaViewPost` in [the default message base template](./misc/menu_templates/message_base.in.hjson) and look for the download options (`@method:addToDownloadQueue`, etc.) for details on adding to your system!
* The Gopher server has had a revamp! Standard `gophermap` files are now served along with any other content you configure for your Gopher Hole! A default [gophermap](https://en.wikipedia.org/wiki/Gopher_(protocol)#Source_code_of_a_menu) can be found [in the misc directory](./misc/gophermap) that behaves like the previous implementation. See [Gopher docs](./docs/servers/gopher.md) for more information.
## 0.0.11-beta ## 0.0.11-beta
* Upgraded from `alpha` to `beta` -- The software is far along and mature enough at this point! * Upgraded from `alpha` to `beta` -- The software is far along and mature enough at this point!

View File

@ -263,7 +263,7 @@ module.exports = () => {
port : 8070, port : 8070,
publicHostname : 'another-fine-enigma-bbs.org', publicHostname : 'another-fine-enigma-bbs.org',
publicPort : 8070, // adjust if behind NAT/etc. publicPort : 8070, // adjust if behind NAT/etc.
bannerFile : 'gopher_banner.asc', staticRoot : paths.join(__dirname, './../gopher'),
// //
// Set messageConferences{} to maps of confTag -> [ areaTag1, areaTag2, ... ] // Set messageConferences{} to maps of confTag -> [ areaTag1, areaTag2, ... ]

View File

@ -27,6 +27,7 @@ const _ = require('lodash');
const fs = require('graceful-fs'); const fs = require('graceful-fs');
const paths = require('path'); const paths = require('path');
const moment = require('moment'); const moment = require('moment');
const async = require('async');
const ModuleInfo = exports.moduleInfo = { const ModuleInfo = exports.moduleInfo = {
name : 'Gopher', name : 'Gopher',
@ -81,8 +82,8 @@ exports.getModule = class GopherModule extends ServerModule {
this.publicHostname = config.contentServers.gopher.publicHostname; this.publicHostname = config.contentServers.gopher.publicHostname;
this.publicPort = config.contentServers.gopher.publicPort; this.publicPort = config.contentServers.gopher.publicPort;
this.addRoute(/^\/?\r\n$/, this.defaultGenerator); this.addRoute(/^\/?msgarea(\/[a-z0-9_-]+(\/[a-z0-9_-]+)?(\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(_raw)?)?)?\/?\r\n$/, this.messageAreaGenerator);
this.addRoute(/^\/msgarea(\/[a-z0-9_-]+(\/[a-z0-9_-]+)?(\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(_raw)?)?)?\/?\r\n$/, this.messageAreaGenerator); this.addRoute(/^(\/?[^\t\r\n]*)\r\n$/, this.staticGenerator);
this.server = net.createServer( socket => { this.server = net.createServer( socket => {
socket.setEncoding('ascii'); socket.setEncoding('ascii');
@ -161,22 +162,56 @@ exports.getModule = class GopherModule extends ServerModule {
return `${itemType}${text}\t${selector}\t${hostname}\t${port}\r\n`; return `${itemType}${text}\t${selector}\t${hostname}\t${port}\r\n`;
} }
defaultGenerator(selectorMatch, cb) { staticGenerator(selectorMatch, cb) {
this.log.debug( { selector : selectorMatch[0] }, 'Serving default content'); this.log.debug( { selector : selectorMatch[1] || '(gophermap)' }, 'Serving static content');
let bannerFile = _.get(Config(), 'contentServers.gopher.bannerFile', 'gopher_banner.asc'); const requestedPath = selectorMatch[1];
bannerFile = paths.isAbsolute(bannerFile) ? bannerFile : paths.join(__dirname, '../../../misc', bannerFile); let path = this.resolveContentPath(requestedPath);
fs.readFile(bannerFile, 'utf8', (err, banner) => { if (!path) {
if(err) { return cb('Not found');
return cb('You have reached an ENiGMA½ Gopher server!'); }
fs.stat(path, (err, stats) => {
if (err) {
return cb('Not found');
} }
banner = splitTextAtTerms(banner).map(l => this.makeItem(ItemTypes.InfoMessage, l)).join(''); let isGopherMap = false;
banner += this.makeItem(ItemTypes.SubMenu, 'Public Message Area', '/msgarea'); if (stats.isDirectory()) {
return cb(banner); path = paths.join(path, 'gophermap');
isGopherMap = true;
}
fs.readFile(path, isGopherMap ? 'utf8' : null, (err, content) => {
if (err) {
let content = 'You have reached an ENiGMA½ Gopher server!\r\n';
content += this.makeItem(ItemTypes.SubMenu, 'Public Message Area', '/msgarea');
return cb(content);
}
if (isGopherMap) {
// Convert any UNIX style LF's to DOS CRLF's
content = content.replace(/\r?\n/g, '\r\n');
// variable support
content = content
.replace(/{publicHostname}/g, this.publicHostname)
.replace(/{publicPort}/g, this.publicPort);
}
return cb(content);
});
}); });
} }
resolveContentPath(requestPath) {
const staticRoot = _.get(Config(), 'contentServers.gopher.staticRoot');
const path = paths.resolve(staticRoot, `.${requestPath}`);
if (path.startsWith(staticRoot)) {
return path;
}
}
notFoundGenerator(selector, cb) { notFoundGenerator(selector, cb) {
this.log.debug( { selector }, 'Serving not found content'); this.log.debug( { selector }, 'Serving not found content');
return cb('Not found'); return cb('Not found');

View File

@ -3,7 +3,7 @@ layout: page
title: Gopher Server title: Gopher Server
--- ---
## The Gopher Content Server ## The Gopher Content Server
The Gopher *content server* provides access to publicly exposed message conferences and areas over Gopher (gopher://). The Gopher *content server* provides access to publicly exposed message conferences and areas over Gopher (gopher://) as well as any other content you wish to serve in your Gopher Hole!
## Configuration ## Configuration
Gopher configuration is found in `contentServers.gopher` in `config.hjson`. Gopher configuration is found in `contentServers.gopher` in `config.hjson`.
@ -11,14 +11,36 @@ Gopher configuration is found in `contentServers.gopher` in `config.hjson`.
| Item | Required | Description | | Item | Required | Description |
|------|----------|-------------| |------|----------|-------------|
| `enabled` | :+1: | Set to `true` to enable Gopher | | `enabled` | :+1: | Set to `true` to enable Gopher |
| `staticRoot` | :+1: | Sets the path serving as the static root path for all Gopher content. Defaults to `enigma-bbs/gopher`.<br>See also **Gophermap's** below |
| `port` | :-1: | Override the default port of `8070` | | `port` | :-1: | Override the default port of `8070` |
| `publicHostName` | :+1: | Set the **public** hostname/domain that Gopher will serve to the outside world. Example: `myfancybbs.com` | | `publicHostname` | :+1: | Set the **public** hostname/domain that Gopher will serve to the outside world. Example: `myfancybbs.com` |
| `publicPort` | :+1: | Set the **public** port that Gopher will serve to the outside world. | | `publicPort` | :+1: | Set the **public** port that Gopher will serve to the outside world. |
| `messageConferences` | :+1: | An map of *conference tags* to *area tags* that are publicly exposed via Gopher. See example below. | | `messageConferences` | :-1: | An map of *conference tags* to *area tags* that are publicly exposed via Gopher. See example below. |
Notes on `publicHostName` and `publicPort`: Notes on `publicHostname` and `publicPort`:
The Gopher protocol serves content that contains host/domain and port even when referencing it's own documents. Due to this, these members must be set to your publicly addressable Gopher server! The Gopher protocol serves content that contains host/domain and port even when referencing it's own documents. Due to this, these members must be set to your publicly addressable Gopher server!
## Gophermap's
[Gophermap's](https://en.wikipedia.org/wiki/Gopher_(protocol)#Source_code_of_a_menu) are how to build menus for your Gopher Hole. Each map is a simple text file named `gophermap` (all lowercase, no extension) with DOS style CRLF endings.
Within any directory nested within your `staticRoot` may live a `gophermap`. A template may be found in the `enigma-bbsmisc` directory.
ENiGMA will pre-process `gophermap` files replacing in following variables:
* `{publicHostname}`: The public hostname from your config.
* `{publicPort}`: The public port from your config.
:information_source: See [Wikipedia](https://en.wikipedia.org/wiki/Gopher_(protocol)#Source_code_of_a_menu) for more information on the `gophermap` format.
:bulb: Tools such as [gfu](https://rawtext.club/~sloum/gfu.html) may help you with `gophermap`'s
### Example Gophermap
An example `gophermap` living in `enigma-bbs/gopher`:
```
iWelcome to a Gopher server! {publicHostname} {publicPort}
1Public Message Area /msgarea {publicHostname} {publicPort}
.
```
### Example ### Example
Let's suppose you are serving Gopher for your BBS at `myfancybbs.com`. Your ENiGMA½ system is listening on the default Gopher `port` of 8070 but you're behind a firewall and want port 70 exposed to the public. Lastly, you want to expose some fsxNet areas: Let's suppose you are serving Gopher for your BBS at `myfancybbs.com`. Your ENiGMA½ system is listening on the default Gopher `port` of 8070 but you're behind a firewall and want port 70 exposed to the public. Lastly, you want to expose some fsxNet areas:
@ -26,9 +48,10 @@ Let's suppose you are serving Gopher for your BBS at `myfancybbs.com`. Your ENiG
contentServers: { contentServers: {
gopher: { gopher: {
enabled: true enabled: true
publicHostName: myfancybbs.com publicHostname: myfancybbs.com
publicPort: 70 publicPort: 70
// Expose some public message conferences/areas
messageConferences: { messageConferences: {
fsxnet: { // fsxNet's conf tag fsxnet: { // fsxNet's conf tag
// Areas of fsxNet we want to expose: // Areas of fsxNet we want to expose:

View File

@ -242,8 +242,8 @@
port: XXXXX port: XXXXX
enabled: false enabled: false
// bannerFile path in misc/ by default. Full paths allowed. // The root directory to serve gophermaps and other content
bannerFile: XXXXX staticRoot: XXXXX
// //
// The Gopher Content Server can export message base // The Gopher Content Server can export message base

View File

@ -1,9 +0,0 @@
_____________________ _____ ____________________ __________\_ /
\__ ____/\_ ____ \ /____/ / _____ __ \ / ______/ // /___jp!
// __|___// | \// |// | \// | | \// \ /___ /_____
/____ _____| __________ ___|__| ____| \ / _____ \
---- \______\ -- |______\ ------ /______/ ---- |______\ - |______\ /__/ // ___/
/__ _\
<*> ENiGMA½ // HTTPS://GITHUB.COM/NUSKOOLER/ENIGMA-BBS <*> /__/
-------------------------------------------------------------------------------

12
misc/gophermap Normal file
View File

@ -0,0 +1,12 @@
i_____________________ _____ ____________________ __________\_ /
i\__ ____/\_ ____ \ /____/ / _____ __ \ / ______/ // /___jp!
i// __|___// | \// |// | \// | | \// \ /___ /_____
i/____ _____| __________ ___|__| ____| \ / _____ \
i---- \______\ -- |______\ ------ /______/ ---- |______\ - |______\ /__/ // ___/
i /__ _\
i <*> ENiGMA½ // HTTPS://GITHUB.COM/NUSKOOLER/ENIGMA-BBS <*> /__/
i
i-------------------------------------------------------------------------------
i
1Public Message Area /msgarea {publicHostname} {publicPort}
.

View File

@ -146,6 +146,12 @@ install_node_packages() {
fi fi
} }
copy_template_files() {
if [[ ! -f "./gopher/gophermap" ]]; then
cp "./misc/gophermap" "./gopher/gophermap"
fi
}
enigma_footer() { enigma_footer() {
log "ENiGMA½ installation complete!" log "ENiGMA½ installation complete!"
echo -e "\e[1;33m" echo -e "\e[1;33m"
@ -189,6 +195,7 @@ install_nvm
configure_nvm configure_nvm
download_enigma_source download_enigma_source
install_node_packages install_node_packages
copy_template_files
enigma_footer enigma_footer
} # this ensures the entire script is downloaded before execution } # this ensures the entire script is downloaded before execution