Merge pull request #330 from NuSkooler/gopher-enhancements
Gopher enhancements
This commit is contained in:
commit
50b35d8cac
|
@ -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.
|
||||
* 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!
|
||||
* 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
|
||||
* Upgraded from `alpha` to `beta` -- The software is far along and mature enough at this point!
|
||||
|
|
|
@ -263,7 +263,7 @@ module.exports = () => {
|
|||
port : 8070,
|
||||
publicHostname : 'another-fine-enigma-bbs.org',
|
||||
publicPort : 8070, // adjust if behind NAT/etc.
|
||||
bannerFile : 'gopher_banner.asc',
|
||||
staticRoot : paths.join(__dirname, './../gopher'),
|
||||
|
||||
//
|
||||
// Set messageConferences{} to maps of confTag -> [ areaTag1, areaTag2, ... ]
|
||||
|
|
|
@ -27,6 +27,7 @@ const _ = require('lodash');
|
|||
const fs = require('graceful-fs');
|
||||
const paths = require('path');
|
||||
const moment = require('moment');
|
||||
const async = require('async');
|
||||
|
||||
const ModuleInfo = exports.moduleInfo = {
|
||||
name : 'Gopher',
|
||||
|
@ -81,8 +82,8 @@ exports.getModule = class GopherModule extends ServerModule {
|
|||
this.publicHostname = config.contentServers.gopher.publicHostname;
|
||||
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 => {
|
||||
socket.setEncoding('ascii');
|
||||
|
@ -161,22 +162,56 @@ exports.getModule = class GopherModule extends ServerModule {
|
|||
return `${itemType}${text}\t${selector}\t${hostname}\t${port}\r\n`;
|
||||
}
|
||||
|
||||
defaultGenerator(selectorMatch, cb) {
|
||||
this.log.debug( { selector : selectorMatch[0] }, 'Serving default content');
|
||||
staticGenerator(selectorMatch, cb) {
|
||||
this.log.debug( { selector : selectorMatch[1] || '(gophermap)' }, 'Serving static content');
|
||||
|
||||
let bannerFile = _.get(Config(), 'contentServers.gopher.bannerFile', 'gopher_banner.asc');
|
||||
bannerFile = paths.isAbsolute(bannerFile) ? bannerFile : paths.join(__dirname, '../../../misc', bannerFile);
|
||||
fs.readFile(bannerFile, 'utf8', (err, banner) => {
|
||||
if(err) {
|
||||
return cb('You have reached an ENiGMA½ Gopher server!');
|
||||
const requestedPath = selectorMatch[1];
|
||||
let path = this.resolveContentPath(requestedPath);
|
||||
if (!path) {
|
||||
return cb('Not found');
|
||||
}
|
||||
|
||||
fs.stat(path, (err, stats) => {
|
||||
if (err) {
|
||||
return cb('Not found');
|
||||
}
|
||||
|
||||
banner = splitTextAtTerms(banner).map(l => this.makeItem(ItemTypes.InfoMessage, l)).join('');
|
||||
banner += this.makeItem(ItemTypes.SubMenu, 'Public Message Area', '/msgarea');
|
||||
return cb(banner);
|
||||
let isGopherMap = false;
|
||||
if (stats.isDirectory()) {
|
||||
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) {
|
||||
this.log.debug( { selector }, 'Serving not found content');
|
||||
return cb('Not found');
|
||||
|
|
|
@ -3,7 +3,7 @@ layout: page
|
|||
title: Gopher 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
|
||||
Gopher configuration is found in `contentServers.gopher` in `config.hjson`.
|
||||
|
@ -11,14 +11,38 @@ Gopher configuration is found in `contentServers.gopher` in `config.hjson`.
|
|||
| Item | Required | Description |
|
||||
|------|----------|-------------|
|
||||
| `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` |
|
||||
| `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. |
|
||||
| `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!
|
||||
|
||||
## 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.
|
||||
|
||||
:information_source: See [RFC 1436](https://tools.ietf.org/html/rfc1436) for the original Gopher spec.
|
||||
|
||||
: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
|
||||
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 +50,10 @@ Let's suppose you are serving Gopher for your BBS at `myfancybbs.com`. Your ENiG
|
|||
contentServers: {
|
||||
gopher: {
|
||||
enabled: true
|
||||
publicHostName: myfancybbs.com
|
||||
publicHostname: myfancybbs.com
|
||||
publicPort: 70
|
||||
|
||||
// Expose some public message conferences/areas
|
||||
messageConferences: {
|
||||
fsxnet: { // fsxNet's conf tag
|
||||
// Areas of fsxNet we want to expose:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Copy ../misc/gophermap to this directory and edit to your liking!
|
|
@ -242,8 +242,8 @@
|
|||
port: XXXXX
|
||||
enabled: false
|
||||
|
||||
// bannerFile path in misc/ by default. Full paths allowed.
|
||||
bannerFile: XXXXX
|
||||
// The root directory to serve gophermaps and other content
|
||||
staticRoot: XXXXX
|
||||
|
||||
//
|
||||
// The Gopher Content Server can export message base
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
_____________________ _____ ____________________ __________\_ /
|
||||
\__ ____/\_ ____ \ /____/ / _____ __ \ / ______/ // /___jp!
|
||||
// __|___// | \// |// | \// | | \// \ /___ /_____
|
||||
/____ _____| __________ ___|__| ____| \ / _____ \
|
||||
---- \______\ -- |______\ ------ /______/ ---- |______\ - |______\ /__/ // ___/
|
||||
/__ _\
|
||||
<*> ENiGMA½ // HTTPS://GITHUB.COM/NUSKOOLER/ENIGMA-BBS <*> /__/
|
||||
|
||||
-------------------------------------------------------------------------------
|
|
@ -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}
|
||||
.
|
|
@ -146,6 +146,12 @@ install_node_packages() {
|
|||
fi
|
||||
}
|
||||
|
||||
copy_template_files() {
|
||||
if [[ ! -f "./gopher/gophermap" ]]; then
|
||||
cp "./misc/gophermap" "./gopher/gophermap"
|
||||
fi
|
||||
}
|
||||
|
||||
enigma_footer() {
|
||||
log "ENiGMA½ installation complete!"
|
||||
echo -e "\e[1;33m"
|
||||
|
@ -189,6 +195,7 @@ install_nvm
|
|||
configure_nvm
|
||||
download_enigma_source
|
||||
install_node_packages
|
||||
copy_template_files
|
||||
enigma_footer
|
||||
|
||||
} # this ensures the entire script is downloaded before execution
|
||||
|
|
Loading…
Reference in New Issue