CONCEPT
        Telnet Negotiations

DESCRIPTION
        Telnet negotiations are being used for transmitting status
        and general informations between telnet-client und server.
        They are specified in their respective RFCs (Request For
        Comments) which define the standard-way-to-do-it.

        Check following for the options mentioned herein:
        RFC 0854 for the telnet protocol specification
        RFC 0857 for ECHO         /* no, not mentioned here, but used */
        RFC 1073 for NAWS         /* in Amylaar's windowsize          */
        RFC 1091 for TERMINAL TYPE
        RFC 1096 for XDISPLAY LOCATION
        RFC 1408 for ENVIRON
        RFC 1184 for LINEMODE
        RFC 0855 for End Of Record

        All negotiations start with the special character IAC which is
        defined in /usr/include/arpa/telnet.h (or in
        src/driver/telnet.h for 3.2(.1)) and has the decimal value of
        255. Negotiations are based on different telnetoptions (their
        values are defined in telnet.h too). Before a negotiation can
        start the client and the server have to agree that they
        support the option.
        This works in the following way:

        If a client wants to send something to the server it has to
        send 'IAC WILL option' (For terminaltype negotation this would
        be the 3 bytes 255,251,24; again, check telnet.h) to confirm
        that it is able to do that. If the server is supporting that
        option and wants to receive something it sends 'IAC DO option'
        (255,253,option)

        If one side is receiving an 'IAC WILL option' and has not yet
        sent with DO or DONT it has to respond with either 'IAC DO
        option' if it will support this negotiation or 'IAC DONT
        option' if it won't.

        If one side is receiving an 'IAC DO option' and has not yet
        sent a WILL or WONT it has to reply with either 'IAC WILL
        option' if it supports the option or 'IAC WONT option' if not.

        A small example: Lets assume we want to negotiating
        terminaltype. (TELOPT_TTYPE with value 24). client is the
        telnet executable on the playerside, the server is the
        gamedriver.

                client                        server
            IAC WILL TTYPE                
                                    IAC DO TTYPE

        Or:
                                    IAC DO TTYPE
            IAC WILL TTYPE

        After this we are ready to transfer the terminaltype from the
        client to the server as explained below.

        Now we are ready to start the real negotiations. I explain the
        3 options I have currently implemented.

        First TerminalType aka TTYPE aka 24 aka TELOPT_TTYPE assuming
        the client and the server have exchanged WILL/DO.

        The server is now free to send 'IAC SB TELOPT_TTYPE
        TELQUAL_SEND IAC SE' which will be replied with 'IAC SB
        TELOPT_TTYPE TELQUAL_IS terminaltype IAC SE' where
        terminaltype is a non-zero terminated string (it's terminated
        by the IAC) (For values look up telnet.h) AND switch the
        client's terminalemulation to 'terminaltype'. terminaltype is
        case-insensitive. terminal-type may be UNKNOWN. The server may
        repeat the SEND request and the client will respond with the
        next preferred terminaltype. If this is the same as the
        previous received, it marks the end of the list of
        terminaltypes. The next SEND request will start the
        terminaltypes from the beginning.

        Example: (we have exchanged WILL/DO already)
                  client                                server
                                        IAC SB TTYPE SEND IAC SE
        IAC SB TTYPE IS VT200 IAC SE
                                        IAC SB TTYPE SEND IAC SE
        IAC SB TTYPE IS VT100 IAC SE
                                        IAC SB TTYPE SEND IAC SE
        IAC SB TTYPE IS VT52 IAC SE
                                        IAC SB TTYPE SEND IAC SE
        IAC SB TTYPE IS VT52 IAC SE
        /* this marks that we have all terminaltypes. We decide to use the
         * vt200 mode so we have to skip to VT200
         */
                                        IAC SB TTYPE SEND IAC SE
        IAC SB TTYPE IS VT200 IAC SE


        Next important option is NAWS (31) or WindowSizeNegotiation.

        This one is a bit easier than terminaltype. After having
        received a IAC DO NAWS from the server, the client will reply
        with IAC WILL NAWS and immediately after that send IAC SB NAWS
        columns_high columns_low lines_high lines_low IAC SE where
        xx_low refers to the lowbyte of xx and xx_high refers to the
        highbyte of xx. This will be automagically resent at every
        windowresize (when the client gets a SIGWINCH for example) or
        at your request with 'IAC SB NAWS SEND IAC SE'.

        Example: (WILL/DO exchanged)
                client                                server
        IAC SB NAWS 0 80 0 24 IAC SE         /* the standard vt100 windowsize */
                                        /* no reply */

        And, a bit less important but most complex, the LINEMODE (34)
        option. It was implemented it due to the fact, that
        some weird DOS telnets would not work otherwise. Implemented
        are only the absolute basic feature, which is the actual
        switching the telnet to linemode. After exchanging WILL/DO the
        server sends a modechange request to the client using IAC SB
        LINEMODE LM_MODE MODE_EDIT IAC SE, which should turn on local
        commandline-editing for the client. If a client supports
        LINEMODE it HAS to support this modechange. The client will
        reply with IAC SB LINEMODE LM_MODE MODE_EDIT|MODE_ACK IAC SE
        (x|y is bitwise or). Thats it for linemode. (You will perhaps
        receive other IAC SB LINEMODEs with other LM_xxx ... you may
        ignore them. (At least IRIX 5.x sends IAC SB LINEMODE LM_SLC
        .... IAC SE which declares the local characterset.)).

        Example: (WILL/DO negotiated)

                client                                        server
                                                IAC SB LINEMODE LM_MODE
                                                       MODE_EDIT IAC SE
        IAC SB LINEMODE LM_MODE
          MODE_EDIT|MODE_ACK IAC SE

        Note: The option is much more funnier as it looks here, it for
          example supports a mixed mode between linemode and
          charactermode... flushing the input at certain characters (at
          ESC or TAB for shell-like commandline completition). We suggest
          reading RFC 1184.

        You might be interested in TELOPT_XDISPLAYLOC and TELOPT_ENVIRON too.

        Now, how to implement this using LDMud?

        0. Patch src/driver/comm1.c, function init_telopts() to include
            telopts_do[TELOPT_XXX] = reply_h_telnet_neg;
            telopts_dont[TELOPT_XXX] = reply_h_telnet_neg;
            telopts_will[TELOPT_XXX] = reply_h_telnet_neg;
            telopts_wont[TELOPT_XXX] = reply_h_telnet_neg;
           for every telnet negotiation you want to use.
           Do not overwrite the TELOPT_ECHO and TELOPT_SGA hooks.

           Alternatively, set the driver hook H_NOECHO in master.c:
           this diverts _all_ telnet data into the mudlib.

        1. Add a new driver hook to master.c just below the others.
                set_driver_hook(H_TELNET_NEG,"telnet_neg"),
        2. Make a telnet.h for your mudlib... just change the arrays in
                src/driver/telnet.h.
        3. define a function

                void telnet_neg(int cmd, int option, int * optargs)

           in your interactive objects (login.c , shells, player.c or
           whereever). And note, in ALL objects, through which a
           player is handed through (in TAPPMud these are login.c and
           player.c). [Ok, master.c is interactive for a very short
           time too, but it won't accept input, will it?]
           'cmd' will be TELCMD_xxxx (see telnet.h), 'option' one of
           TELOPT_xxxx and 'optargs' will be an array of ints (bytes in
           fact) when 'cmd' is SB.
           Parse 'cmd'/'option' and reply with appropiate answers
           using binary_message() (appropiate meaning sending the
           right DO/DONT/WILL/WONT if not sent before and using the SB
           return values).
        3.1. Sent IAC DO TTYPE IAC DO NAWS IAC DO LINEMODE at the
           first time you can do it (before cat()ing /WELCOME perhaps).
        3.2. Note all sent and received WILL/WONT/DO/DONT options for
           conforming to the standard, avoiding endless loops and for
           easy debugging :)
        3.3. Pass those recevied/sent data and other data when the
           interactive object is changed (from login.c to player.c or
           at other bodychanges). Clear the data when the player goes
           linkdead or quits. You won't need to save this data.
        3.4. Lower_case() terminaltypes... ;)
        3.5. Use reasonable defaultvalues if the client does not
           support one of the options. (columns 80,lines 24 if not
           NAWS, unknown or vt100 for no terminaltype)

        The WILL/WONT/DO/DONT data is best saved in a mapping looking
        like this:
          ([ "received": ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ])
           , "sent"    : ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ])
          ])

        (Ok, it can be done better. But not without confusing *me*
        more.)

        Before sending anything check
          TN["sent"][option,0_if_do_dont_or_1_if_will_wont]
        so you don't enter endless loops, save network traffic and the
        like.

        The windowsize is best saved in the players environment
        variables so that he can modify them later on. (Or in two
        integers in the player object...). Use for these values is
        clear I think.

        The terminaltypes received using above mentioned method are
        best stored in an array. The actual set terminaltype is best
        stored in an environment variable where the player can modify
        it. Upon modifying it the IAC SB TTYPE SEND IAC SE cycle
        should be started to match the emulation to the entered new
        terminaltype. You then may use data retrieved from
        /etc/termcap (man 5 termcap) or /usr/lib/terminfo/*/* (SysVID,
        man 5 terminfo) to implement terminalcontrol codes dependend
        on the terminaltype. /etc/termcap may prove to be the easiest
        way tough /usr/lib/terminfo/*/* is the newer (and better) SysV
        way of doing it.

        [Anyone got a description of the internal terminfo format for
        me? -Marcus]

        LINEMODE replies may be left alone if only using the mode
        change to MODE_EDIT

        Some statistics about what clients support telnet negotiations:

        Tinyfugue and some other mudclients usually do not support
        negotiations.
        Except for TF, which supports the Telnet End-Of-Record option
        as marker for the end of the prompt. So if you send IAC EOR
        after every prompt, it will print the prompt always in the
        input window. (Do not forget to negotiate that. First IAC WILL
        TELOPT_EOR/wait for IAC DO TELOPT_EOR). Newer versions of
        TF will support NAWS and there will be a patch for TTYPE
        negotiation available soon.

        All telnets able to do negotiations I've encountered support
        the TTYPE option.
        HP9.x,Irix5.x,Linux,EP/IX,CUTELNET/NCSATELNET (Novell) and
        perhaps more support NAWS.
        At least Irix5.x,Linux,CU/NCSATELNET support LINEMODE.
        SUN does not support NAWS and LINEMODE neither in SunOS 4.1.3
        nor in Solaris 2.3.

        For getting RFCs you can for example use
        ftp://ftp.uni-erlangen.de/pub/doc/rfc/


BUGS
        Not all aspects of the options are mentioned to keep this doc
        at a reasonable size. Refer to the RFCs to get more confused.

CREDITS
        Provided by Marcus@TAPPMud (Marcus Meissner,
        <msmeissn@cip.informatik.uni-erlangen.de>).
