[alt.sources] cit301.part2

eric@sactoh0.SAC.CA.US (Eric J. Nihill) (11/26/90)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	docs
# This archive created: Sun Nov 25 12:49:02 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test ! -d 'docs'
then
	echo shar: "creating directory 'docs'"
	mkdir 'docs'
fi
echo shar: "entering directory 'docs'"
cd 'docs'
echo shar: "extracting 'copyright.doc'" '(1148 characters)'
if test -f 'copyright.doc'
then
	echo shar: "will not over-write existing file 'copyright.doc'"
else
cat << \SHAR_EOF > 'copyright.doc'
                          Citadel/UX release 3.01-1d
     Copyright (c) 1989 by Art Cancro @ Westchester Data Control Systems
                             All Rights Reserved
     
   This package may be used by anyone for any reason and distributed as
widely as possible provided no financial compensation for the package is
imposed. Modifications made to the package may NOT be distributed, in any
format, without written permission from the author. Use of this package
certifies your agreement to comply by the licence rules outlined in this file.
The author assumes no responsibility for the effects of any programs included
in this package on any of the user's hardware or software.
  
  This package is user supported shareware. Its contents represent countless
hours of work. If you do set up a system using this release, please send a
contribution of $20 to:
 
                    Westchester Data Control Systems
                            One Kirby Plaza
                    East White Plains, NY 10604-2718
                    ATTN:  Citadel/UX communications
 
  UNIX is a trademark of AT&T  /  Xenix is a trademark of Microsoft, Inc.
SHAR_EOF
fi
echo shar: "extracting 'hack.doc'" '(22466 characters)'
if test -f 'hack.doc'
then
	echo shar: "will not over-write existing file 'hack.doc'"
else
cat << \SHAR_EOF > 'hack.doc'
 hack.doc for Citadel/UX
 written by Art Cancro / October 1988
   
  Most of this document was taken from the original hack.doc from Citadel-CP/M
and Citadel-86. Most of the concepts used in Citadel/UX are the same, but the
filenames and structures are different. 
  
  Here are the major files to be discussed:
  
  msgmain         The big circular file that contains message text
  quickroom       Contains room info such as room names, stats, etc.
  fullroom        One fullrm file per room: message numbers and pointers.
  usersupp        Contains info for each user on the system.
  
    The fundamental structure of the system is very simple. msgmain
is a circular file.  New messages are simply written around the buffer
in an endless circle, overwriting old messages in their way.  There is
no other way of deleting messages, and text is never shuffled on disk.
Messages are numbered consecutively and start with an FF (hex)
byte.  Except for this FF start-of-message byte, all bytes in the message
file have the high bit set to 0.  This means that in principle it is
trivial to scan through the message file and locate message N if it
exists, or return error.  (Complexities, as usual, crop up when we
try for efficiency...)
    Each room is basically just a list of message numbers.  Each time
we enter a new message in a room, we slide all the old message-numbers
down a slot, and probably the oldest one falls off the bottom.	Reading
a room is just a matter looking up the messages one by one and printing
them out.  If the message has been overwritten already, we just ignore it.
    Implementing the "new message" function is also trivial in principle:
we just keep track, for each caller in the userlog, of the highest-numbered
message which existed on the >last< call.  (Remember, message numbers are
simply assigned sequentially each time a message is created.  This
sequence is global to the entire system, not local within a room.)  If
we ignore all message-numbers in the room less than this, only new messages
will be printed.  Voila! 

		message format on disk	(msgmain)

  Each message begins with an FF byte. The next byte will then be MES_NORMAL,
MES_ANON, or MES_ANON2, depending on whether the message in anonymous or not.
The next byte is either a 0 or 1. If it is 0, the message will be printed
with the formatter as usual. If it is a 1, the message is dumped byte by
byte directly to the screen. After these three opening bytes, the remainder of
the message consists of a sequence of character strings.  Each string
begins with a type byte indicating the meaning of the string and is
ended with a null.  All strings are printable ASCII: in particular,
all numbers are in ASCII rather than binary.  This is for simplicity,
both in implementing the system and in implementing other code to
work with the system.  For instance, a database driven off Citadel archives
can do wildcard matching without worrying about unpacking binary data such
as dates first.  To provide later downward compatability
all software should be written to IGNORE fields not currently defined.

		  The type bytes currently defined are: 	

BYTE	Mnemonic	Comments

T	Date/Time	A long containing the date and time of the message in
                        standard UNIX format (the number of seconds since
                        January 1, 1970)
P	Path		Complete path of message, as in the UseNet news
			protocol. A user should be able to send UUCP mail to
			this path. (Note that your system name will not be
			tacked onto this until you're sending the message to
			someone else)
I	ID on orig	A long containing the message ID on the system the
			message originated on.
A	Author		Name of originator of message.
R	Recipient	Only present in Mail messages.
O	Room		Room of origin.
N	Nodename	Contains name of system message originated on.
D	Destination	Contains name of the system this message should
			be sent to, for mail routing.
M	Message Text	Normal ASCII, newlines seperated by CR's or LF's,
                        null terminated as always.
  
			EXAMPLE

Let <FF> be a 0xFF byte, and <0> be a null (0x00) byte.  Then a message
which prints as...

Apr 12, 1988 23:16 From Test User In Network Test> @lifesys
Have a nice day!

 might be stored as...
<FF><40><0>I12345<0>Pneighbor!lifesys!test_user<0>T576918988<0>    (continued)
-----------|Mesg ID#|--Message Path---------------|--Date------

AThe Test User<0>ONetwork Test<0>Nlifesys<0>MHave a nice day!<0>
|-----Author-----|-Room name-----|-nodename-|--Message text---------

 Weird things can happen if fields are missing, especially if you use the
networker. But basically, the date, author, room, and nodename may be in any
order. But the leading fields and the message text must remain in the same
place.

			    Networking

Citadel nodes network by sharing one or more rooms via uucp. Any Citadel node
can choose to share messages with any other Citadel node, through the sending
of spool files. The sending system takes all messages it hasn't sent yet, and
spools them to the recieving system, which posts them in the rooms.

Complexities arise primarily from the possibility of densely connected
networks: one does not wish to accumulate multiple copies of a given
message, which can easily happen.  Nor does one want to see old messages
percolating indefinitely through the system.

This problem is handled by a simple brute-force mechanism: each node
keeps a list of all messages it has seen recently, recording origin
system, creation date, and author. When processing recieved messages, ones
which have already been seen, or which are too old to be remembered,
are skipped.  Messages can percolate outward through a large network
with no global routing or control, but do not reproduce wildly or
cycle indefinitely. Also, when sending messages to another system, the
networker can look at a message's P (path) field for the receiving system's
name. If it finds it, the system already has seen that message, so it doesn't
send it a new copy. Note that some older versions do not support the P field,
so make sure that it works adequately without this last feature (although
it should be implemented because it saves lots of connect time).

The above discussion should make the function of the 
fields reasonably clear:

 o  Travelling messages need to carry original message-id, system of origin,
    date of origin, and author with them, to keep reproduction and
    cycling under control. Path is highly reccomended.

(Uncoincidentally) the format used to transmit messages for networking
purposes is precisely that used on disk, except that there may be any amount
of garbage between the null ending a message and the <FF> starting the next
one. This allows greater compatibility if slight problems crop up. The current
distribution includes netproc.c, which is basically a database integrator;
please see network.doc on its operation and functionality (if any).

			portability problems

The major problem seems to be that no two tty devices are exactly alike. You
will probably need to adjust the settings to fit your system; check the
sttybbs() routine for this.

			"Room" records (quickroom/fullroom)

The rooms are basically indices into msgmain, the message file.
As noted in the overview, each is essentially an array of pointers into
the message file.  The pointers consist of a 32-bit message ID number
(we will wrap around at 32 bits for these purposes) together with a 32-bit
file pointer within msgmain telling us where the message begins.

Since messages are numbered sequentially and written circularly, the
set of messages existing in msgmain will always form a continuous
sequence at any given time.  Thus, by remembering the ID numbers of the
oldest and newest messages in the message file, we can check to see
if a message exists >before< going to msgmain, saving ourselves (and the
disk drive) the pain of futile seeks in search of the nonexistent.
This information is recorded in oldest and newest, 32 bit numbers.
You'll be seeing more of these...

The newest is simply incremented each time we enter a
new message in the message files.  Oldest is incremented
each time we overwrite an FF (start-of-message) byte in the course
of writing a new message into the files.  This corresponds to dead
reckoning -- current code never checks to see that the message number
of the message we are overwriting is what we think it is.  In a garbaged
file with extra FF bytes around, this could cause oldest to
count too rapidly, eventually perhaps overtaking newest,
at which time the system will look completely empty. This should never
happen, but it's always safe to take backups.

That should be enough background to tackle a full-scale room.  From citadel.h:

struct quickroom {
	char QRname[20];		/* Max. len is 19, plus null term   */
	char QRpasswd[10];		/* Only valid if it's a private rm  */
	long QRroomaide;		/* User number of room aide         */
	long QRhighest;			/* Highest message NUMBER in room   */
	char QRgen;			/* Generation number of room        */
	unsigned QRflags;		/* See flag values below            */
	char QRdirname[15];		/* Directory name, if applicable    */
		};

#define QR_BUSY		1		/* Room is being updated, WAIT      */
#define QR_INUSE	2		/* Set if in use, clear if avail    */
#define QR_PRIVATE	4		/* Set for any type of private room */
#define QR_PASSWORDED	8		/* Set if there's a password too    */
#define QR_GUESSNAME	16		/* Set if it's a guessname room     */
#define QR_DIRECTORY	32		/* Directory room                   */
#define QR_UPLOAD	64		/* Allowed to upload                */
#define QR_DOWNLOAD	128		/* Allowed to download              */
#define QR_VISDIR	256		/* Visible directory                */
#define QR_ANONONLY	512		/* Anonymous-Only room              */
#define QR_ANON2	1024		/* Anonymous-Option room            */
#define QR_NETWORK	2048		/* Shared network room              */
#define QR_PREFONLY	4096		/* Preferred users only             */
struct fullroom {
	long FRnum[MSGSPERRM];		/* Message NUMBERS		    */
	long FRpos[MSGSPERRM];		/* Message POSITIONS in master file */
		};

[Note that all components start with "QR" for quickroom, to make sure we
 don't accidentally use an offset in the wrong structure. Be very careful
 also to get a meaningful sequence of components --
 there is no checking on this sort of stuff either.]

QRgen handles the problem of rooms which have died and been reborn
under another name.  This will be clearer when we get to the userlog.
For now, just note that each room has a generation number which is
bumped by one each time it is recycled.

QRflags is just a bag of bits recording the status of the room.  The
defined bits are:

QR_BUSY		This is to insure that two processes don't update the same
		record at the same time, even though this hasn't been
		implemented yet.
QR_INUSE	1 if the room is valid, 0 if it is free for re-assignment.
QR_PRIVATE	1 if the room is not visible by default, 0 for public.
QR_PASSWORDED	1 if entry to the room requires a password.
QR_GUESSNAME	1 if the room can be reached by guessing the name.
QR_DIRECTORY	1 if the room is a window onto some disk/userspace, else 0.
QR_UPLOAD	1 if users can upload into this room, else 0.
QR_DOWNLOAD	1 if users can download from this room, else 0.
QR_VISDIR	1 if users are allowed to read the directory, else 0.
QR_ANONONLY	1 if all messages are to recieve the "****" anon header.
QR_ANON2	1 if the user will be asked if he/she wants an anon message.
QR_NETWORK	1 if this room is shared on a network, else 0.
QR_PREFONLY	1 if the room is only accessible to preferred users, else 0.

QRname is just an ASCII string (null-terminated, like all strings)
giving the name of the room.

QRdirname is meaningful only in QR_DIRECTORY rooms, in which case
it gives the directory name to window.

QRpasswd is the room's password, if it's a QR_PASSWORDED room. Note that
if QR_PASSWORDED or QR_GUESSNAME are set, you MUST also set QR_PRIVATE.
QR_PRIVATE by itself designates invitation-only. Do not EVER set all three
flags at the same time.

QRroomaide is the user number of the room's room-aide (or zero if the room
doesn't have a room aide). Note that if a user is deleted, his/her user number
is never used again, so you don't have to worry about a new user getting the
same user number and accidentally becoming a room-aide of one or more rooms.

The only field new to us in quickroom is QRhighest, recording the
most recent message in the room.  When we are searching for rooms with
messages a given caller hasn't seen, we can check this number
and avoid a whole lot of extra disk accesses.

   The fullroom is the array of pointers into the message file. We keep one
file for each fullroom array to keep the quickroom file small (and access time
efficient). FRnum and FRpos are the message numbers and positions on disk of
each message in the room. (For NIL, we stick zeroes in both fields.)

			user records (usersupp)

This is the fun one.  Get some fresh air and plug in your thinking cap
first.	(Time, space and complexity are the eternal software rivals.
We've got lots of log entries x lots of messages spread over up to 100
rooms to worry about, and with multiuser disk access time is important...
so perforce, we opt for lots of complexity to keep time and space in bounds.)

To understand what is happening in the log code takes a little persistence.
You also have to disentangle the different activities going on and
tackle them one by one.

 o	We want to remember some random things such as terminal screen
	size, and automatically set them up for each caller at login.

 o	We want to be able to locate all new messages, and only new
	messages, efficiently.	Messages should stay new even if it
	takes a caller a couple of calls to get around to them.

 o	We want to remember which private rooms a given caller knows
	about, and treat them as normal rooms.	This means mostly
	automatically seeking out those with new messages.  (Obviously,
	we >don't< want to do this for unknown private rooms!)	This
	has to be secure against the periodic recycling of rooms
	between calls.

 o	We want to support private mail to a caller.

 o	We want to provide some protection of this information (via
	passwords at login) and some assurance that messages are from
	who they purport to be from (within the system -- one shouldn't
	be able to forge messages from established users).

Lifting another page from citadel.h gives us:

struct usersupp {			/* User record                      */
	int USuid;			/* uid account is logged in under   */
	char password[20];		/* password (for BBS-only users)    */
	long lastseen[MAXROOMS];	/* Last message seen in each room   */
	char generation[MAXROOMS];	/* Generation # (for private rooms) */
	char forget[MAXROOMS];		/* Forgotten generation number      */
	long mailnum[MAILSLOTS];	/* Message #'s of each mail message */
	long mailpos[MAILSLOTS];	/* Disk positions of each mail      */
	unsigned flags;			/* See US_ flags below              */
	int screenwidth;		/* For formatting messages          */
	int timescalled;		/* Total number of logins           */
	int posted;			/* Number of messages posted (ever) */
	char fullname[30];		/* Bulletin Board name for messages */
	long eternal;			/* Eternal user number              */
	long lastcall;			/* Last time the user called        */
				};

#define US_PERM		1		/* Permanent user; don't scroll off */
#define US_AIDE		2		/* Aide privileges                  */
#define US_LASTOLD	16		/* Print last old message with new  */
#define US_EXPERT	32		/* Experienced user		    */
#define US_UNLISTED	64		/* Unlisted userlog entry           */
#define US_NOPROMPT	128		/* Don't prompt after each message  */
#define US_TWIT		256		/* Problem user			    */
#define US_NONET	512		/* Deny this user net privileges    */
#define US_PREF		1024		/* Preferred user                   */
 
Looks simple enough, doesn't it?  One topic at a time:

 Random configuration parameters:
-screenwidth is the caller's screen width.  We format all messages to this
width, as best we can. flags is another bit-bag, recording whether we want
prompts, people who want to suppress the little automatic hints all through
the system, "problem users," etc.
 
  Attachments, names & numbers:
-USuid is the uid the account was established under. For most users it will
be the same as BBSUID, but it won't be for users that logged in from the shell.
-fullname is the user's full login name.
-eternal is the user's "eternal" user number. It is called eternal because
once someone has a user number, it is never used again after the user is
deleted. This allows an easy way to numerically represent people.
-password is the user's password. It is only requested if the user does not
have shell access and is BBS-only.

  Feeping Creatures:
-timescalled is the number of times the user has called.
-posted is the number of messages the user has posted, public or private.

  Misc stuff:
-lastcall holds the date and time (standard Unix format) the user called, so
we can purge people who haven't called in a given amount of time.

  Finding new messages:
This is the most important.  Thus, it winds up being the most
elaborate.  Conceptually, what we would like to do is mark each
message with a bit after our caller has read it, so we can avoid
printing it out again next call.  Unfortunately, with lots of user
entries this would require adding lots of space to each message... and
we'd wind up reading off disk lots of messages which would never
get printed.  So we resort to approximation and lots of storage.

The approximation comes in doing things at the granularity of
rooms rather than messages.  Messages in a given room are "new"
until we visit it, and "old" after we leave the room... whether
we read any of them or not.  This can actually be defended: anyone
who passes through a room without reading the contents probably just
isn't interested in the topic, and would just as soon not be dragged
back every visit and forced to read them.  Given that messages are
numbered sequentially, we can simply record the most recent message ID#
of each room as of the last time we visited it. Very simple.

Putting it all together, we can now compute whether a given room
has new messages for our current caller without going to fullroom at all:

 > We get the usersupp.lastseen[] for the room in question
 > We compare this with the room's quickroom.QRhighest, which tells us
   what the most recent message in the room is currently.


	     REMEMBERING WHICH PRIVATE ROOMS TO VISIT

This looks trivial at first glance -- just record one bit per room per
caller in the log records.  The problem is that rooms get recycled
periodically, and we'd rather not run through all the log entries each
time we do it.	So we adopt a kludge which should work 99% of the time.

As previously noted, each room has a generation number, which is bumped
by one each time it is recycled.  As not noted, this generation number
runs from 0 -> 127 (and then wraps around and starts over). 
  When someone visits a room, we set usersupp.generation for the room
equal to that of the room.  This flags the room as being available.
If the room gets recycled, on our next visit the two generation numbers
will no longer match, and the room will no longer be available -- just
the result we're looking for.  (Naturally, if a room is public,
all this stuff is irrelevant.)

This leaves only the problem of an accidental matchup between the two
numbers giving someone access to a Forbidden Room.  We can't eliminate
this danger completely, but it can be reduced to insignificance for
most purposes.	(Just don't bet megabucks on the security of this system!)
Each time someone logs in, we set all "wrong" generation numbers to -1.
So the room must be recycled 127 times before an accidental matchup
can be achieved.  (We do this for all rooms, INUSE or dead, public
or private, since any of them may be reincarnated as a Forbidden Room.)

Thus, for someone to accidentally be lead to a Forbidden Room, they
must establish an account on the system, then not call until some room
has been recycled 127 to 128 times, which room must be
reincarnated as a Forbidden Room, which someone must now call back
(having not scrolled off the userlog in the mean time) and read new
messages.  The last clause is about the only probable one in the sequence.
The danger of this is much less than the danger that someone will
meet the right (wrong?) people and get into the room anyway...

                     FORGOTTEN ROOMS

  This is exactly the opposite of private rooms. When a user chooses to
forget a room, we put the room's generation number in usersupp.forget for
that room. When doing a <K>nown rooms list or a <G>oto, any matchups cause
the room to be skipped. Very simple.

		     SUPPORTING PRIVATE MAIL

Can one have an elegant kludge?  This must come pretty close.

Private mail is sent and recieved in the Mail> room, which otherwise
behaves pretty much as any other room.	To make this work, we store
the actual message pointers in mailnum[] and mailpos[] in the caller's
log record, and then copy them into the Mail> room array whenever we
enter the room.  This requires a little fiddling to get things just
right.	We have to update quickroom[1].QRhighest at login
to reflect the presence or absence of new messages, for example.  And
make_message() has to be kludged to ask for the name of the recipient
of the message whenever a message is entered in Mail>.	But basically
it works pretty well, keeping the code and user interface simple and
regular.


		   PASSWORDS AND NAME VALIDATION

The usersupp file is kept in alphabetical order, allowing us to do a binary
search whenever we need to look up a user. When a new user logs in, he/she
has to be inserted into the proper alphabetical place. Just in case something
goes wrong, a "sort userlog" command is included in sysoputil, although it
never really gets used.
  So when a user types his/her login name, or when we're looking up a mail
recipient, or anything else which requires finding a usersupp entry for a
given login name, all we have to do is do a binary search. The routine
finduser() does this quickly and efficiently.

  This makes it difficult to forge messages from an existing user.  (Fine
point: nonprinting characters are converted to printing characters, and
leading, trailing, and double blanks are deleted.)
SHAR_EOF
fi
echo shar: "extracting 'install.doc'" '(5061 characters)'
if test -f 'install.doc'
then
	echo shar: "will not over-write existing file 'install.doc'"
else
cat << \SHAR_EOF > 'install.doc'
                      Citadel/UX Installation Procedure
     Written by Art Cancro [Inspector Gadget @ UNCENSORED! (914) 761-6877]
                 See copyright.doc for copyright information
 
 
 OVERVIEW
  
   Citadel/UX is a bulletin board system based on, but not ported from, the
Citadel-CP/M and Citadel-86 systems. It was developed on an Altos 586/40
running Altos Xenix 3.0b, and has been installed and tested on various UNIX
and Xenix systems, including various System III and System V ports. The
current distribution includes:
    - The Citadel/UX main program
    - A networker that utilizes UUCP and can share messages with other
      Citadel/UX systems, as well as UseNet sites (a StoneHenge networker
      is available by special request)
    - A program to allow logged in users to chat with whoever is on the console
    - Setup programs
    - Various sysop utilities, and a shell script menu to select them from
    - Documentation
 
 Some knowledge of the UNIX system is necessary to install and manage the
system. It is preferable that the sysop have superuser access to the operating
system. The following are required to install Citadel/UX:
    - Some sort of UNIX-based operating system
    - C compiler
    - make (if you wish to utilize the Makefile, else compile manually)
    - The "curses" library (two of the utilities utilize cursor control)
    - Enough disk space to hold all of the programs and data
  
  
 EVERYTHING IN ITS PLACE...

   Hopefully you've unpacked the distribution archive into its own directory.
This is the directory in which all Citadel files are located and in which all
BBS activity will take place. Several subdirectories have already been created
during the unpacking process, and others may be created by the software if
needed. 
   
  
 THE BBS LOGIN
  
   There will be one account in /etc/passwd which all BBS users will use to
login to the system. This account is typically called "bbs" or "citadel" or
something to that effect. You will tell Citadel what the user-id of that
account is, and when someone logs in under that account, Citadel will prompt
for a user name. For all other users in /etc/passwd, Citadel will automatically
set up an account using the "full name" field. No password is required, since
it assumes that if a user is logged in, he/she has already entered a password.
The BBS login should have a unique uid. The home directory should be the
one your BBS resides in (in this example we will use /usr/bbs) and the shell
should be citadel in that directory. Here is an example:
 
 bbs::7:1:BBS Login:/usr/bbs:/usr/bbs/citadel
  
 When we edit the sysconfig.h file, we will tell Citadel what the BBS login's
uid is, so it knows when to prompt for a name and password.
   
   
 SYSTEM CONFIGURATION FILE (sysconfig.h)
  
   You MUST edit sysconfig.h BEFORE you compile any of your programs. It's
massively self-documented; just read the file and edit it as it tells you.
You can change some of the ways the program runs with this file, as well as
configure how big the system is going to be. There is a section labelled
"structure size variables" that will select how many rooms will fit in the
system, how many mail slots each user has, etc. Note that you CANNOT change
these without wiping out your own system, so only set them ONCE.
  
   Once you have edited sysconfig.h, you may then compile all of the programs.
This can be done using the makefile supplied or you can compile them
individually. All of the programs are one source file per executable file,
except the main program citadel, which consists of:
   citadel.c messages.c rooms.c commands.c routines.c routines2.c
 This is also the only program that goes over 64k, so you'll need to compile
it using a big memory model. 
  
   File permissions are always a bother to work with. You don't want the
board to crash because someone couldn't access a file, but you also don't
want shell users peeking into the binaries to do things like reading others'
mail, finding private rooms, etc. One solution is to make the data files
accessible only by the super user, and setting the set-user-id bit for each
program that accesses them. Sysop utilities should only be accessible by
people in a given group, and the set-user-id bit should also be set.
   
   
 SETUP AND LOGIN
  
   You may now run the setup program, which will prompt you regarding whether
it should create certain files. If this is the first time setting up the BBS,
you will want to answer yes to all of the prompts. It will display what it is
doing as it creates the various files needed to run the system.
   
   At this point, your system is ready to run. Run the citadel program from
the shell and it will automatically create your account. You'll probably
want to log right back out so you can give yourself aide access before
continuing. 
    
   
 THE PEANUT GALLERY
  
   That's just about all the information you need to install the system. If
you have any comments, suggestions, bomb threats, etc., send them to
UNCENSORED! BBS at (914) 761-6877.
 
SHAR_EOF
fi
echo shar: "extracting 'netrelease.doc'" '(2893 characters)'
if test -f 'netrelease.doc'
then
	echo shar: "will not over-write existing file 'netrelease.doc'"
else
cat << \SHAR_EOF > 'netrelease.doc'
 This document is intended for Citadel/UX sysops who have a version of
netproc lower than 1.5 and intend to modify the system to the latest networking
standards INSTEAD of upgrading to Citadel/UX 3.00 and Network 1.5. If you are
installing Citadel/UX 3.00 and Network 1.5, ignore this document.
  
   
   INFORMATION ON UPGRADING TO CITADEL/UX NETWORK PROTOCOL VERSION 1.5
   -------------------------------------------------------------------
    
 1. MESSAGE ROUTING
    When netproc receives a message containing a "D" (destination system)
field, and the destination system is not your system, ship off the message
in the direction of that system. Netproc 1.5 looks at the network/mail.sysinfo
file to determine which neighbor to send the message to in order for it to
arrive on the destination system. If the "D" field is not present, or the
destination system is your own, process the message.
 
 2. RECEIVING BINARY MAIL
    When processing messages, if a message is received that has an "R"
(recipient) field, search for that user and place the message in his/her
mailbox. If the user does not exist, either return the message or alert the
local management. Netproc 1.5 dumps undeliverable mail in the Aide> room.
  
 3. SENDING BINARY MAIL
    Users should be able to go to the Mail> room and enter a recipient in
the form of "user @ sysname". The message generated will contain the following
fields:
           P - Sender's name, lower case, spaces replaced by underscores
           T - Date & time of message (standard Unix format)
           A - Sender's name
           O - Name of mail room
           N - Name of your system
           R - Name of recipient at destination system
           D - Name of destination system
   If the destination system is not a neigbor of your system, check the
network/mail.sysinfo file to determine which of your neighbors it should be
routed to.
  
 4. NETPROC 1.5
    New features supported:
    - Message routing using "D" field
    - Receiving binary mail. finduser() is used to locate a user in the
usersupp file. This routine utilizes a binary search and assumes that the
file is in alphabetical order. It returns the position of the correct usersupp
record or (-1L) for User Not Found. If you are using a version earlier than
2.22, you must change finduser() to search using the conventional method.
  
 5. CITADEL/UX 3.00
    Too many changes have been made to the main program to make the binary
mail sender part of the new network release. In particular, the routine
to enter messages is no longer part of the main() loop. This was done in part
to be able to use the program netmailer.c, which replaces uucitamail.c. The
system will continue to support the sending of standard UUCP mail.
  
 6. THE PEANUT GALLERY
    Questions, comments, suggestions, etc. should be directed to me at
UNCENSORED! BBS (914) 761-6877 300/1200/2400.
  
SHAR_EOF
fi
echo shar: "extracting 'network.doc'" '(8886 characters)'
if test -f 'network.doc'
then
	echo shar: "will not over-write existing file 'network.doc'"
else
cat << \SHAR_EOF > 'network.doc'
                        Citadel/UX Network v1.5 Manual
     Written by Art Cancro [Inspector Gadget @ UNCENSORED! (914) 761-6877]
                 See copyright.doc for copyright information
  
     
  OVERVIEW
   
   The fundamental structure of the networker is fairly simple, however, it
has enough features to make it a bit complicated. This is probably the most
difficult part of the entire Citadel/UX package. So before we dive in head
first, let's look at the various network files and directories.
  
 netsetup.c                   Setup program for the networker.
 netproc.c                    Does all of the actual network processing.
 rnews.c                      Feeds standard input into the networker, also
                              has the ability to translate UseNet news format
                              into Citadel/UX binary format.
 netmailer.c                  Called by the main program when a user sends a
                              network mail message.
 rmail.c                      A patch to allow Citadel/UX users to receive
                              mail through the normal UUCP mail facility.
 cux2ascii.c                  A filter which translates Citade/UX binary format
                              to UseNet news format.
 network                      Directory in which all network files reside.
 network/systems              Contains network info for each neighboring system
 network/systems/sysname      Network file for a node called "sysname".
 network/mail.aliases         Aliases for the mailer.
 network/rnews.xref           Cross-references room names to newsgroup names.
 network/prevmsgs             The current use table.
 network/prevmsgs.pos         Current file position in the use table.
 network/mail.sysinfo         Contains routing information for network mail.
   
  
  SETUP
  
  There are two options in sysconfig.h which must be properly set. The
first is NODENAME, which must be the same as your uucp system name. The other
is PREVMSGS, which is the number of records in the use table (prevmsgs). The
use table records the message number and origin of messages recently received
over the network, to prevent the same message arriving from multiple sources
to be posted twice. A good value to start with is about a thousand. NOTE: the
value of PREVMSGS times the size of a record in the table (currently 14) must
not exceed the maximum size of an array on your system. The entire table
is read into an array in memory during inbound processing.
   
   Once you have edited sysconfig.h, compile and run the netsetup program. It
will ask you if you want to clear the use table; answer yes. It will create
network/prevmsgs and network/prevmsgs.pos, files that you will never have to
deal with yourself (netproc handles them completely).
   
   
   SETTING UP SYSTEMS FILES
   
   For each of your neighboring Citadel/UX systems you must create a systems
file. The file is called network/systems/sysname, where sysname is the other
system's node name. The first line contains a command that transfers a spool
file to the network/spoolin directory on the remote system. The string "%s"
will be replaced by the name of the spool file by netproc. You may only use
%s ONCE in the command line. Usually, some sort of UUCP transfer will be used
to do the transfer, but you may use any facility you want, *** as long as the
file ends up in the network/spoolin directory on the remote system ***. In
a typical system, you will probably use uux to pipe the file through the
command "rnews -c" on the remote system. When the remote system receives this
command, it will copy standard input to the network/spoolin directory, and
then begin processing the file as soon as it is there.
   After the command line you should enter the names of all the rooms you
intend to share with this system. Each room name should be followed by a
line containing a zero - this extra field is the "last message sent" (which
will be updated by netproc when it is run). Here is a sample systems file for
a node called uncnsrd:
  
cat %s |uux - uncnsrd!rnews -c
Network Test
0
Gateway
0
The Room
0
   
  The rooms "Network Test", "Gateway", and "The Room" will be spooled to
the remote system. These rooms should be designated as network rooms with
the .<A>ide <E>ditRoom command.
   
  
  USING NETPROC
  
   Calling netproc with no arguments simply looks in the network/spoolin
directory for newly arrived messages, and posts them if it finds any. If
any message has already arrived on the system (which can happen in densely
connected networks), they will be rejected. 
   To collect new messages and send them off to a remote system, the usage
  
 netproc sysname
  
 will do outbound network processing for system "sysname". It is reccomended
that you use the cron program to handle your network processing on a routine
basis automatically. 
   
   
  NETWORKING WITH A USENET NEWS SITE
   
   Two filters are provided that will allow a room to be equivalent to a
UseNet newsgroup. rnews, when called without the -c argument, will assume
that standard input is in the news format, and convert it before processing.
Likewise, the filter cux2ascii.c converts Citadel format to news format,
allowing you to use command lines such as
   
 cat %s |cux2ascii |uux - uunet!rnews
   
  ...the remote system need not be a Citadel/UX. By default, room names are
the same as the newsgroup names. However, if you wish the room name to be
different, you may specify so in the network/rnews.xref file. Here is a
sample of this file:
       
comp.unix.wizards,UNIX wizards
alt.drugs,Drugs and Narcotics
alt.music,Music
   
  It is rather simple, each line taking the form of newsgroupname,sysname.
There may not be any spaces in the newsgroup name, but there may be spaces
in the corresponding room name.
   
   To allow Citadel/UX users to receive standard UUCP mail, you can install
the rmail patch provided. Simply compile rmail.c, move your old rmail to
rmail.real, and put the compiled rmail in its place. Make sure the setuid
bit is set, just as it is in rmail.real. Users may then receive network
mail. If there are any spaces in users' names, people sending them mail may
simply replace them with underscores. Note that this patch will not disturb
the normal functioning of the UUCP mail facility. Also, for Citadel to
Citadel mail, use the regular net mailer (although you may have both
installed simultaneously).
   
   
  MAIL ALIASES
   
   The file network/mail.aliases is a simple list of aliases for the various
mailers to use. Each line takes the form
  
alias,name
  
   Obviously, neither the alias nor the name can contain commas. The name
may also be the system name "sysop", where messages sent to sysop will
be posted in the Aide> room. 
   
   
  CITADEL/UX NETWORK MAIL
   
   Citadel/UX now has the ability to transport mail in a simple and
transparent fashion not unlike the way public messages are sent. Users may
enter recipient names exactly as they appear on top of messages (i.e.,
user name @ system name). In addition, mail routing is provided, allowing
users to send mail to systems which do not directly connect with their own.
   
  When entering a message in the Mail> room, a user may type a recipient
name on the local system, or on a remote system. If the recipient is not
local, citadel.c calls netmailer.c, which is a standalone program that handles
network mail. The system will fork, allowing netmailer to run in the
background while the user goes on to something else. 
  
  Network mail needs to have the file network/mail.sysinfo to work with. There
are two types of entries in this file. A "use" entry tells the system which
neighbor to route a message through to get to a particular non-neighboring
system. A "bin" entry tells the system that a particular neighbor supports
net mail. Here is a sample network map, where our system is called "myself":
     
                                           _____testbbs
                                          / 
           othersys ----- myself ----- thebox
              /                          \_______theirsys
          funboard
   
   In this example, our neighbors are "othersys" and "thebox". othersys
also connects to funboard, and thebox connects to testbbs and theirsys. If
everyone supports netmail, the network/mail.sysinfo file would look like this:
   
funboard
use othersys

testbbs
use thebox

theirsys
use thebox

othersys
bin Mail

theirsys
bin Mail
   
  The "bin" entries specify neighbors, whereas the "use" entries specify
routing. This way, anyone can send mail to anyone else.
   
   
  CONCLUSION
   
   That should cover everything you need to get running. By the way, a
program to network with StoneHenge systems is available by special request.
As always, comments and suggestions may be directed to UNCENSORED! BBS at
(914) 761-6877.
SHAR_EOF
fi
echo shar: "extracting 'sysop.doc'" '(7640 characters)'
if test -f 'sysop.doc'
then
	echo shar: "will not over-write existing file 'sysop.doc'"
else
cat << \SHAR_EOF > 'sysop.doc'
                        Citadel/UX Sysop/Aide Manual
     Written by Art Cancro [Inspector Gadget @ UNCENSORED! (914) 761-6877]
                 See copyright.doc for copyright information
  
   
 OVERVIEW
 
   Citadel/UX, when installed properly, will do most of its maintenance by
itself. The message file loops upon itself forever, scrolling off old messages
to make space for new ones. The room files work in the same way. Other types
of maintenance can be done by cron. I have left my system unattended for long
periods of time without any software failures. 
   
  The system has five access levels. Most users are at the bottom and have no
special privileges. Aides are selected people who have special access within
the Citadel program. Room Aides only have this access in a certain room. 
Preferred users can be selected by Aides for access to preferred only rooms. A
sysop is anyone who has access to the various sysop utilities - these are in
their own executable files, which should have their permissions set to allow
only sysops to run them. I reccomend either creating a sysops group in
/etc/group, or using the "sys" group for this purpose. 
   
   Aides have access to EVERY room on the system, public and private (all
types). They also have access to commands starting with .<A>ide in addition
to being able to delete and move messages. The system room, Aide>, iS
accessible only by those designated by aides.
   
   
 AIDE COMMANDS
 
   Aides have the following commands available to them that are not available
to normal users. They are:
  
 .<A>ide <E>dit room         Allows an aide to change certain parameters of
                             the current room. Lobby>, Mail>, and Aide> may
                             not be edited.
 .<A>ide <K>ill room         Deletes the current room. Lobby>, Mail>, and
                             Aide> may not be deleted.
 .<A>ide <R>oom <I>nvite     Invites a user to the room if it is private.
 .<A>ide <R>oom <K>ickOut    Kicks a user out of the room if it is private.
 .<A>ide <U>serEdit          Edits certain parameters of a user's account.
 .<A>ide <W>hoKnowsRoom      Lists all users who have access to, and who have
                             not chosen to zap, the current room.
   
   
 EDITING ROOMS
 
   This command allows and aide to change certain parameters of the current
room. The current room name will be displayed, and then prompted:
 New name <return=same>?
  If you press return, the room name will remain unchanged. If you enter a
new name, enter it exactly as you want it to appear. No case conversion will
be done, unlike the .<E>nter <R>oom command.
   At this point, all of the room's parameters will be changed. Then the
following prompts will appear:
  
 New room type? <1>pub <2>guessname <3>passwd <4>inv-only :
   Entering <1> will give all users access to the room.
   Entering <2> will only give access to those users who specifically type
.<G>oto roomname. Until then, it will not appear in their <K>nown rooms list
and it will not be included in their <G>oto loop.
   Entering <3> will prompt you for a room password. For users to access a
passworded room, they must know the name AND password.
   Entering <4> will provide no means for users to access the room unless an
aide invites them with the .<A>ide <R>oom <I>nvite user command.
   
 Preferred users only?     Answering <Y>es will only allow "preferred users"
                           access to the room. Note that the room should
                           be public unless you wish to restrict access even
                           further.
 Directory room?           Answering <Y>es will make the room a "directory
                           room" which can be used for uploads and downloads.
                           If you answer <Y>es, you will also receive the
                           following three prompts:
     Directory name?       Enter the name of the subdirectory for this room's
                           files (it will be in the u/d area of the bbs, i.e.
                           if Citadel is in /usr/bbs and you enter "text", the
                           room's u/d's will be in /usr/bbs/files/text).
  Uploading allowed?       Answering <Y>es will allow users to upload into
                           the room.
Downloading allowed?       Answering <Y>es will allow users to download from
                           the room.
  Visible Directory?       Answering <Y>es will allow users to read the
                           file directory of the room.
  
Network Room?              The networker will access any room regardless of
                           this flag. However, if you are on a network you
                           should answer <Y>es for networked rooms for two
                           purposes: first, to make Citadel/UX print the name
                           of the originating system in the header of each
                           message, and secondly, to make message entry in the
                           room inaccessible to those users from whom you have
                           revoked network privileges.
     
 <1>Normal <2>Anon-only <3>Anon-option ? 
   Entering <1> will cause all messgaes in the room to have normal headers.
   Entering <2> will cause all message headers to read "****"
   Entering <3> will cause messages to have either normal headers or headers
that read "<anonymous>", upon each user's choice during message entry.
  
   The current room aide's name will be displayed if there is one, and then
you will be prompted:
 Room aide (RETURN to leave unchanged): 
    Enter the new room aide's name, or press return to leave it unchanged.
    
   
   EDITING USERS
 
   This command allows and aide to change certain parameters of any user's
account. Entering this command will ask for the name of a user to edit, and
then present the following three prompts:
 Problem user (y/n)?                 Answering <Y>es will flag the person as
                                     a "problem user" - if TWITDETECT is
                                     enabled, ALL of this user's messages will
                                     automatically be moved into the problem
                                     users room.
 Deny network privs (y/n)?           Answering <Y>es will prevent the user from
                                     entering messages in network rooms.
 Permanent user (never scroll off)?  Answering <Y>es will prevent the user from
                                     being purged off the userlist by the
                                     sysoputil program.
 Preferred user (y/n)?               Answering <Y>es will give the user
                                     "preferred user" status.
   
   
   DELETING AND MOVING MESSAGES
   
   Aides have the ability to delete and move messages; however, they must have
message prompting turned on in order to do this. After each message, the
normal prompt appears:
   <A>gain, <N>ext message, <S>top ->
  Entering <D> will delete the message. A (y/n) prompt will appear to confirm
that you really want to delete the message.
  Entering <M> will prompt for a room to move the message to.
     
   
   SYSOP UTILITIES
   
   There are a number of utilities which may be accessed from the shell. It
is up to the system operator to decide which should be "sysop" utilities and
which should be accessible to all shell users. Please see utils.doc for a
description of these programs.
   
   CONCLUSION
  
   Comments from the Peanut Gallery should be directed to (at) my humble
mailbox at UNCENSORED! BBS  (914) 761-6877.
  
SHAR_EOF
fi
echo shar: "extracting 'utils.doc'" '(9231 characters)'
if test -f 'utils.doc'
then
	echo shar: "will not over-write existing file 'utils.doc'"
else
cat << \SHAR_EOF > 'utils.doc'
                        Citadel/UX Utilities Manual 
     Written by Art Cancro [Inspector Gadget @ UNCENSORED! (914) 761-6877]
                 See copyright.doc for copyright information
   
   
 OVERVIEW
    
   The following utilities will be discussed in this document:
 aidepost   Post standard input to the Aide> room. NOTE: called by chat.c
 whobbs     Who is on the system, includes BBS names. NOTE: called by citadel.c
 msgstats   Print lowest & highest message numbers, and current file position
 stats      Print the calling statistics & graph. NOTE: requires "curses"
 rdml       Read a user's mail
 msgform    Format a binary message to the screen (stdin or in a file)
 userlist   Print the userlist. NOTE: called by citadel.c (.RU option)
 readlog    Read the caller log
 useradmin  Full-screen user account editor. NOTE: requires the "curses" lib
 sysoputil  Various and sundry sysop utilities.
   
   It is up to you to decide which utilities should be made accessible only
to sysops. It is important that you set the file permissions correctly. All
utilities should have access to the BBS data files, whether through ownership
or set-user-id. In addition, aidepost should be accessible by the chat program,
and userlist & whobbs should be accessible by citadel. I will attempt to
address each program individually.
   
   
  AIDEPOST
  
   The nature of this program is rather simple. Standard input (stdin) is
converted into a message, filed in the main message file (msgmain), and posted
in the Aide> room. This is useful for keeping transcripts of system activity
that has to do with the BBS. You may also wish to have a really BIG msgmain
file, and have all of your normal system logs go in there also. 
   The chat program included with Citadel, chat.c, uses this program to post
a transcript of each chat that takes place.
   
    
  WHOBBS
  
   This is a simple replacement for the standard "who" command. It looks in
/etc/utmp for users currently logged in. Instead of printing login names,
it prints full names (i.e., the fifth field in /etc/passwd). If a logged in
user's id happens to be BBSUID (the user is a BBS-only user), whobbs instead
looks in the "utmpsupp" file for the name they are using under Citadel.
   Citadel/UX calls this program when any user types the <W>ho command.
   
   
  MSGSTATS
   
   This program displays the lowest and highest message numbers which currently
exist in the master file, the current file pointer (what byte in msgmain the
next message entered will start at), and whether the file is locked or
unlocked. Normally the file will only be locked for a second or two during a
message <S>ave, or network processing, but if there is a glitch somewhere and
the file remains locked, this program will tell you so and you can use the
unlock option in sysoputil.
   
   
  STATS
  
   This program is one of two which utilize the "curses" library. It prints
various statistics on the screen based on the calllog file, such as number
of calls, number of calls with certain baud rates, number of carrier drops,
bad password attempts, etc. All statistics are provided in three figures:
times per call, times per day, and total times.
   After this screen appears, you may press return for the next screen. This
is a graphic representation of system usage. For each half hour segment of
time during the day, it will display the average percentage of that segment
which is used. Local logins are not included in this display. (As distributed,
a "remote" login is considered anything at 300, 1200, or 2400 baud. You may
wish to modify the code to decide just what really is "remote" on your
particular system.)
  
  
  RDML
   
   This is a simple program to read any user's mail. This, of course, is
unethical. It will prompt for a user name, after which it will display the
user's mail.
   
  
  MSGFORM
  
   On occasion, I have had messages in Citadel/UX binary format that I have
wanted to view. Or needed a way to view a network spool file. Or wanted to
directly examine parts of msgmain. This program is a simple message formatter.
   Called without arguments, msgform will look for binary messages, starting
with an <FF> byte and ending after the final NULL, and print as many as it
finds until it encounters EOF.
   Called with one argument (msgform filename), msgform will display all
messages found in "filename" from beginning to end.
   Called with two arguments (msgform filename startpos), msgform will
display all messages found in filename starting at byte position "startpos".
    
   
  USERLIST
  
   This is a program to print the userlist. There are two flags that may be
set when running this program. When called without any arguments, userlist
will display all users (except those who have chosen to be unlisted), their
user numbers, times called, messages posted, screen width, and date of their
most recent call.
   Setting the -p option (only allowed by root as distributed; you may wish
to change this) also displays passwords, and lists all users regardless of
whether they are unlisted.
   Setting the -n option causes the next argument after -n to be a user
number to search for.
   The Citadel/UX program calls userlist without any arguments for the
.<R>ead <U>serlist option.
   
   
  READLOG
  
   Called without any arguments, readlog dumps the contents of the calllog
file. This file records all times the Citadel/UX program has been started, and
at what baud rate, as well as logins, proper logouts, loss of carrier (SIGHUP),
bad password attempts, and new user logins.
   Readlog called with the -t argument displays a list of the names of the
last twenty users who have logged in.
   
    
  USERADMIN
  
   Useradmin is a full-screen program to view and edit any user's account.
The program will prompt for a user name. After you enter the name, various
parameters of the user's account will appear on the screen. To change one
of the parameters, simply enter its number.
 
  1. User Name - it is ok to change the casing of a user's name, but if you
change the name entirely, the userlist will no longer be in alphabetical
order, which would cause problems in the Citadel program.
  2. User number - if you change a user's number, keep in mind that the user's
account will no longer be attached to any other records which are indexed by
user number, such as the registration.
  3. UID attachment - if you wish to attach a BBS account to an /etc/passwd
account, simply make the uid's the same. Likewise, if a user is to no longer
have an account in /etc/passwd, simply set this to the same as BBSUID.
  4. Password         - These
  5. Screen width         are
  6. Times called         self
  7. Messages posted       explanatory.
  8. Last call - Time of last call (in unix format). If you select this,
it will not prompt for a new time, but set it to the current time.
   
  The rest are boolean flags. Selecting them will toggle the options.
 10. Aide privileges
 11. Permanent user (do not scroll off)
 12. Print last old message on new message request
 13. Expert mode (supress automatic hints)
 14. Do not list in .RU command
 15. Prompting after each message
 16. Problem user flag
 17. Network privileges
 18. Registered in the registration file
 19. Preferred user
   
  In addition, if the user is registered, you can type -1 to make the
corresponding record in the registration file appear on the screen. 
   When you are finished examining or editing an account, type 0 to exit. It
will ask you if you wish to save the changes.
   
   
  SYSOPUTIL
   
   This program handles some of the sysop functions of the system. It can
be called three ways: sysoputil by itself brings up the menu. Also:
 sysoputil -u      Execute option 7 (user purge) and exit
 sysoputil -r      Execute option 1 (room purge) and exit
 sysoputil -g      Execute option 2 (registration list) and exit
  
  The menu choices are as follows:
 
  1. Purge old rooms. This will delete any rooms which have not been written
to (posted in) in two weeks. Notification of purged rooms is posted in the
Aide> room (using the aidepost utility).
 2. Read registration file. This will partially list the registration file
to the screen (partially: street address is omitted).
 3. Delete a user. (self explanatory)
 4. Unlock message file. The msgmain file is locked during message saves to
prevent two users from writing to the file at once. If there is some sort of
crash or bug and it remains locked, this will unlock it.
 5. (future use) 
 6. Sort the userlog. If for some reason the userlog is not in alphabetical
order, this will sort it. New users are alphabetically added into the log
correctly, so there should normally be no reason to run this option.
 7. Purge old users. This will delete any users which have not called in
two months. Notification of purged users is posted in the Aide> room (using
the aidepost utility).
 8. Change casing of a user's name. Upper/lower case may be changed, but you
may not change the name entirely as this will cause the file to no longer be
in alphabetical order.
 9. Quit
   
   
   That should cover all of the included utilities. Comments, suggestions,
etc. should be directed to UNCENSORED! BBS (914) 761-6877.
SHAR_EOF
fi
echo shar: "extracting 'readme.301.doc'" '(298 characters)'
if test -f 'readme.301.doc'
then
	echo shar: "will not over-write existing file 'readme.301.doc'"
else
cat << \SHAR_EOF > 'readme.301.doc'


IMPORTANT MESSAGES FOR USERS UPGRADING FROM 3.00 TO 3.01
  

   The format of the usersupp file has changed slightly. If you are upgrading,
please obtain the 3.00-->3.01 migrate utility from UNCENSORED BBS at
(914) 761-6877. The various access bits in the file have been changed to
user levels.

SHAR_EOF
fi
echo shar: "done with directory 'docs'"
cd ..
exit 0
#	End of shell archive
-- 
     Some do, some don't.           |       eric@sactoh0.SAC.CA.US
    Some will, some won't.          |      ames!pacbell!sactoh0!eric
          I might!                  |      ucbvax!csusac!sactoh0!eric
                                    |     ( A Home For Unwanted 3B's )