Definition of the UDP protocol between naseftpd and LPMud gamedriver
--------------------------------------------------------------------

Messages are exchanged as simple strings. Records within a message are
separated by single Tab characters. The case of the keywords is
irrelevant.

Communication between naseftpd and the gamedriver is always initiated
by the ftpd, and consists of a simple request/answer exchange.
At a time, only one request may be handled between ftpd and
gamedriver.

The first message of newly started ftpd will be a "USER" request. If
this is not answered, the ftpd will depending on its mode terminate or
fall back onto file-based access control.
Failure to answer the other requests will just result in an error
message, if not noted otherwise.


    message :== "NFTPD" number content

<number> is a number identifying messages.
If the gamedriver answers a request, the answer has to carry the same
number as the request.

    content :== ("REQ" | "RPLY") request

The type of a message (request or answer) is determined by its first
word, "REQ" or "RPLY". Requests> may be sent by the ftpd only,
answers by the gamedriver. The first word of the <requests> part
determines the type of the requests and thus in conjunction with the
type denominator the further content.

    request :== "USER" userrequest
              | "PASS" pwdrequest
              | "RESV" resolvereq
              | "READ" filerequest
              | "WRIT" filerequest
              | "LIST" listrequest
              | "PING" pingrequest

The requests will be listed in at least two variants: first the REQ variant,
second and following the RPLY variant(s).

    userrequest :== name
                  | "NONE"
                  | homedir

The ftpd got a new connection, requesting a login as <name>. The GD
has to check if <name> is a valid login and return "NONE" on failure,
and its <homedir> on success.
The <name> 'guest' should always open a guestlogin with restricted
rights. Anonymous logins ('ftp' and 'anonymous') are handled by the
ftpd on its own.

    pwdrequest :== name password
                 | ("OK" | "FAIL")

The ftpd requests a <password> validation for <name>. The gamedriver
has to reply with either "OK" or "FAIL" depending on the success.
If this request is not answered, the login will be denied.

    resolverequest :== name pathname cwdname
                     | pathname

The ftpd requests to resolve the file/directory <pathname> for user
<name> with respect to the current directory <cwdname> (given without
trailing '/').
The gamedriver has to resolve things like '..' or '~name' and return
the resolved name, which will be used in subsequent READ/WRIT/LIST
requests.

    filerequest :== name pathname
                  | ("OK" | "FAIL")

The ftpd requests the availability for file/directory <pathname> (absolute)
for user <name>, depending on the request either for reading or
writing. The answer has to be "OK" if the action is allowed, "FAIL" else.

    listrequest :== name pathname
                  | "LINE" text
                  | "OK"

The ftpd requests a directory listing of <pathname> for user <name>
The gamedriver has to return the lines <text> of the listing in order
using the "LINE" format, and terminates the listing with "OK". Loss of
lines is accepted, <text> may have any content as long as it's
printable (it should end in a newline character, else the output will
look funny :-).

    pingrequest :== (nothing)
                  | "OK"

With this request the ftpd checks the presence of the gamedriver,
which just has to reply "OK".
