Subtext: a multi-user BBS server for Macintosh
Written by joshua stein <jcs@jcs.org> - http://jcs.org/subtext

Subtext is free software; see the LICENSE file for copyright/licensing.

Introduction
==================================================================
Subtext is a multi-user BBS server that is developed and runs on Macintosh
System 6 and above.  It supports dialin access through a serial modem and
Telnet access through MacTCP.

Features
==================================================================
- Multithreaded, multi-user BBS server that runs on System 6+
- Telnet support through MacTCP
- Direct serial modem support (no Communications Toolbox)
- Multiple threaded message boards
- FTN (FidoNet) EchoMail and NetMail support with Binkp hub synching
- Multi-user interactive chat
- Private messaging/mail
- File uploading and downloading with ZMODEM support
- Integrated ANSI-capable local console
- Local log window with smart screen blanking
- Remote syslog (RFC3164) support
- Secure user password storage with SHA256
- View/menu templates with variable expansion
- Configurable main menu key shortcuts
- Telnet brute-force IP banning by sending messages to a firewall host
- Offline IP geolocation database support for connecting telnet IPs

Setup
==================================================================
Most of the administration is done through the BBS itself, by logging in as
a sysop and accessing the sysop menu.  BBS settings, file areas, message
boards, and user accounts can all be modified through this menu.

Editing view tepmlates is done by editing plaintext files in the "views"
directory created where the BBS database lives.

When first run, Subtext will ask to open an existing BBS database.  Clicking
Cancel will prompt to save a new database.  On subsequent runs, Subtext
will automatically try to open the last database that it successfully opened
unless the Command key is held down at startup.

When creating a new database, Subtext will create a default sysop user
with the username "sysop" and password "p4ssw0rd".  It will also fill in
some defaults such as an example BBS name and hostname.

On launch, Subtext will open its logger window and initialize its connection
methods, which are only the console by default.  Clicking the BBS menu and
then Open Console will open a sysop session.  Pressing ! will access the
Sysop menu, and then S will enter the BBS Settings menu.

To enable dialin access, change the Modem Port setting to 1 for the modem
port or 2 for the printer port depending on where the modem is connected.
An appropriate Modem Init String can also be configured, though it should
*not* be configured for Auto-Answer.  The number of rings after which an
"ATA" is issued can be configured in the settings menu.

To enable Telnet access, set the Telnet Port to anything other than 0, such
as the standard telnet port of 23.  Enabling Telnet requires MacTCP to be
installed and configured.

View Templates
==================================================================
In the "views" directory automatically created where the BBS database
lives are a number of text files that are used throughout the BBS:

	- issue.txt: Pre-login welcome screen ("issue" file)
	- menu.txt: Main menu
	- menu_options.txt: Menu commands, see Main Menu Configuration below
	- no_free_nodes.txt: "No Free Nodes" message shown through telnet
	- page_sysop.txt: Text shown before paging the sysop
	- short_menu.txt: Short menu shown after user has seen the main menu
	- signoff.txt: Session signoff
	- signup.txt: Account signup text

After editing view text files, they must be reloaded through the BBS's
console menu option, or through the sysop menu when logged in.

Views support expansion of variables, which are put inside {{ and }}
characters, such as "You are connected to {{node}}."  Supported variables:

	- {{B}}
		Enable bold attribute if the user's terminal supports ANSI (otherwise
		it prints nothing).
	- {{/B}}
		Resets ANSI attributes (including bold) if the user's terminal supports
		ANSI.
	- {{#}}
		Stops varible expansion for the rest of the template.  Useful when
		printing untrusted data (mostly used internally).
	- {{center_XX}}
		Print spaces of length equal to the user's terminal width minus XX, divided
		by two.  For example, if the user's terminal is 80 characters wide and the
		variable {{center_50}} is used before a 50-character-wide menu line, 15
		space characters will be printed.  This is useful for centering a menu on
		wide terminals but preventing empty space on narrower terminals.
	- {{logged_in_time}}
		The amount of time the user has been logged in (still usable in
		signoff template), such as "7m20s".
	- {{new_mail}}
		If the user has any new, unread mail messages, the count such as
		"(4 New)".  Otherwise it is blank.
	- {{node}}
		The current node, such as "ttyt0".
	- {{phone_number}}
		The system's configured phone number, such as "(312) 555-1212".
	- {{time}}
		The current system time in 24-hour format, such as "23:59".
	- {{timezone}}
		The system's configured "Timezone (Abbrev)".
	- {{username}}
		The currently logged-in user's username, or "guest".
	- {{"string"}}
		Print the literal string "string".  This is useful in conditionals.

Variable results can also be truncated or padded to a particular length,
which can be helpful when creating menus with columns.  This is done with
the pipe character and the length, such as:

	{{ username|10 }}

Conditionals are also supported with ternary operators "?" and ":" to print
something if the condition is true (or non-null), otherwise print something
else.  The following conditionals are supported:

	- user
		True if the current user is logged in as a non-guest.
	- sysop
		True if the current user is a sysop.

For example, the following would print one string in menu if the user is
a sysop, otherwise it prints something else.

	{{ sysop ? "Answer sysop page" : "Page the sysop" }}

Or when printing mail for the user:

	{{ user ? new_mail : "Guests cannot access mail." }}

Main Menu Configuration
==================================================================
In addition to creating a custom menu layout, it may be desirable to change
the mapping of keys to functions.  This can be done by editing the
menu_options.txt file and then reloading it through the BBS.  The mapping is
one action per line, with the Action, Menu Key, All Keys list, and Label,
separated by a colon.  Blank lines and those starting with # are ignored.

When there is no custom Main Menu view defined, a list of options will be
programmatically generated by showing all menu options with a Menu Key
defined with the Menu Key and Label.  Options with no Menu Key defined are
still accessible by any key in the All Keys field, but are not shown.

The default mapping is:

	# Action:Menu Key:All Keys:Label
	BOARD_LIST_FTN_AREAS:A:Aa:List FTN Areas
	BOARD_SHOW_FIRST:B:Bb:Message Board
	BOARD_SHOW_1::1:Message Board 1
	BOARD_SHOW_2::2:Message Board 2
	BOARD_SHOW_3::3:Message Board 3
	BOARD_SHOW_4::4:Message Board 4
	BOARD_SHOW_5::5:Message Board 5
	BOARD_SHOW_6::6:Message Board 6
	BOARD_SHOW_7::7:Message Board 7
	BOARD_SHOW_8::8:Message Board 8
	BOARD_SHOW_9::9:Message Board 9
	BOARD_SHOW_10::10:Message Board 10
	CHAT:C:Cc:Multi-User Chat
	FILES_MENU:F:Ff:File Areas
	GOODBYE:G:GgXx:Goodbye
	RECENT_LOGINS:L:Ll:Recent Logins
	MAIL_COMPOSE:N:Nn:Compose New Mail
	MAIL_MENU:M:Mm:Private Mail {{ user ? new_mail : "" }}
	MOTD:O:Oo:Message Of The Day
	PAGE_SEND_OR_ANSWER:P:Pp:{{ sysop ? "Answer Page" : "Page Sysop" }}
	SETTINGS_OR_SIGNUP:S:Ss:{{ user ? "Settings" : "Signup For Account" }}
	WHOS_ONLINE:W:Ww:Who's Online
	SHOW_MENU:?:?:List Menu Options
	SYSOP_MENU::!:Sysop Menu

FTN (FidoNet) Support
==================================================================
Subtext supports FTN EchoMail (distributed message boards) and NetMail
(relayed mail to a user at another node) by way of a Binkp hub.
Directly-delivered "CrashMail" is not supported.  Subtext has a built-in
scanner/tosser with limited support for PKZIP archives of FTN packets.

To get started, configure the FTN Hub Binkp hostname, port, and password
settings.  The network name, hub node address (e.g., "21:3/100"), local node
address, and packet passwords must also be set.  This is enough to enable
NetMail, so users can send mail to a user@XX:X/XXX and have it delivered
through the hub.  Likewise, any incoming NetMail for valid usernames are
delivered, or bounced to the Sysop.

To enable EchoMail areas, create local boards in the Sysop menu and set the
"FTN Area Name" field to match each desired area name exactly.  Note that
once a board has its FTN area name set, it cannot be removed later due to
FTN boards using a different database structure than local, threaded boards.

Once Binkp polling is enabled, Subtext will attempt to login to the Binkp hub
at the configured interval and retrieve any packets sent from the hub, saving
them to the "binkp:inbox" directory.  After fetching any outstanding files,
packets in the "binkp:outbox" directory are sent to the hub.

Packets (or PKZIP archives of packets) in the inbox are tossed and NetMail is
delivered.  Any EchoMail packets for areas that match the FTN Area Name set
in a local board are imported.  Packets that cannot be tossed properly are
moved to the "binkp:bad" directory.  If the "FTN Hub Binkp Delete After
Processing" option is enabled, packets are moved to the "binkp:processed"
directory upon completion, which can be useful during testing.  Otherwise,
processed packets are deleted.

To minimize latency, the Binkp poller will not start while there are any
users logged in.  This means that NetMail messages sent locally will not be
sent out right away unless a Sysop manually triggers Binkp polling from the
Sysop menu.  This comes into play when communicating back and forth with
an FTN AreaFix bot to configure which area messages should be delivered
through the Binkp hub.

Trusted Host
==================================================================
While Subtext can handle direct TCP connections for Telnet, it will likely
be behind a firewall/trusted host.  To limit brute-force login attempts,
Subtext has a hard-coded list of banned account names that most bots will
try in rapid succession, such as "root", "admin", etc.  When a Telnet client
tries one of these usernames, Subtext can send a UDP packet to the trusted
host containing the IP to be banned (in dotted-quad plaintext format, such
as "192.168.1.1").  The trusted host IP and port can be configured in the
Sysop menu.  If either value is empty/zero, this functionality is disabled.
Since there is no authentication with this mechanism, ensure the trusted
host is only listening for UDP packets on the interface facing the Subtext
server!

Also during Telnet negotiation, if the IP of the connecting client matches
the trusted host's IP, Subtext will honor the "REMOTE_ADDR" value present
in the NEWENV variable list and use it as the client's connecting IP address
in logs.  This is useful if the trusted host is acting as a web proxy and can
pass the IP of the web client through to Subtext over Telnet.

When transferring files to and from a host connecting through the trusted
host, Subtext will use its custom NoModem protocol instead of ZMODEM.
This makes it easier to implement file transfers through a web or other
custom frontend.

IP Geolocation
==================================================================
When configured with the path of an appropriate IP geolocation database,
Subtext will use it to lookup connecting IPs.  If a location is found, it will
be logged to the logger and stored in the session log, which will also make
it visible from the "who's online" and "recent logins' screens, so users can
see where other users are connecting from.

The format of this database is specified in the ipdb.c file.  Free pre-built
databases generated from public IP geolocation data can be downloaded
from the Subtext website.  These databases can be quite large (>40MB) and
may be updated frequently, so they are not distributed with Subtext.

Screen Blanking
==================================================================
When running on compact Macs, it is recommended to enable Subtext's
screen blanking option to prevent the log window from burning into the
CRT.  When the system is idle for a configured amount of time, it will blank
the entire screen for a configured amount of time, immediately unblanking
as soon as a new connection comes in, or when a key is pressed or a mouse
button is clicked.  This is better than using a dedicated screen saver which
will not unblank after a period of time, and may be wasting CPU time
drawing flying toasters while Subtext needs to process user activity.

Run on Startup (System 6)
==================================================================
To run Subtext when your Mac starts up, select the Subtext application in
Finder, then open the Special -> Set Startup... menu.  Under "Upon startup,
automatically open:" select Subtext.