'===========================================================================
' Subject: RAPID-Q TELNET CLIENT              Date: 08-27-00 (19:34)       
'  Author: Christopher S. Bradford            Code: RAPIDQ                 
'  Origin: csb@psouth.net                   Packet: RAPIDQ.ABC
'===========================================================================
'** Title: RapidQ Telnet Client / M$ Windows Console
'** Author: Christopher S. Bradford
'**
'** Release Notes:
'**      Ver 1.00:  08/21/2000
'**                 Supports full ANSI terminals, including color, special
'**                 keys, etc... IMPORTANT:  the follow line must be in your
'**                 CONFIG.SYS file:  device=c:\windows\command\ansi.sys
'**                 If you need to add the ansi.sys line to your config.sys,
'**                 then you will need to reboot before running this program.
'**
'**                 There is minor bug when disconnecting from the remote
'**                 telnet server... the client will not exit.  The press of
'**                 any key after disconnect will exit.  I need to find a more
'**                 reliable way of detecting dropped sockets.  Not a big deal.
'**
'**                 Mostly RFC854 compliant, supports protocol negotiation
'**
'**                 Feel free to use, distribute, or otherwise destroy this
'**                 example.
'**
'**                 Compile AS-IS  ...  "rc tel.bas"

$OPTIMIZE ON
$APPTYPE CONSOLE
$TYPECHECK ON

DECLARE SUB NegotiateOptions

CONST False = 0
CONST True  = NOT False

'** NEGOTIATIONS
CONST IAC   = CHR$(255)
CONST tDo   = CHR$(253)      '** values based on RFC854
CONST tDont = CHR$(254)
CONST tWill = CHR$(251)
CONST tWont = CHR$(252)
CONST SE    = CHR$(240)
CONST SB    = CHR$(250)

'** TERMINAL OPTIONS TO NEGOTIATE
CONST TermType = CHR$(24)
CONST TermTypeSB = SB + TermType + CHR$(1) + IAC + SE
CONST TermTypeInfo = IAC + SB +TermType +CHR$(0)+ "ANSI" +IAC +SE

DIM Socket AS QSocket
DIM SockNum AS INTEGER
    SockNum = 0

DIM A$     'Keyboard Input
DIM B$     'Term Read Byte Buffer

DEFSTR tDesc(251 TO 254) = {"WILL","WONT","DO","DONT"}

DIM Keys(255) AS String                     'Key maps for special ANSI
                                            'terminal keys... indexed by
                                            'scancodes
Keys(59)=Chr$(27)+"[M"  'F1                 
Keys(60)=Chr$(27)+"[N"  'F2                 'basically, we're grabbing the
Keys(61)=Chr$(27)+"[O"  'F3                 'scancodes and sending the ansi
Keys(62)=Chr$(27)+"[P"  'F4                 'equivalent
Keys(63)=Chr$(27)+"[Q"  'F5
Keys(64)=Chr$(27)+"[R"  'F6                 'there are more specialkeys, but
Keys(65)=Chr$(27)+"[S"  'F7                 'this basic set should be good for
Keys(66)=Chr$(27)+"[T"  'F8                 'the first release anyway
Keys(67)=Chr$(27)+"[U"  'F9
Keys(68)=Chr$(27)+"[V"  'F10
Keys(71)=Chr$(27)+"[H"  'home
keys(72)=Chr$(27)+"[A"  'Up Arrow
keys(73)=Chr$(27)+"[I"  'PgDn
keys(75)=Chr$(27)+"[D"  'Down Arrow
keys(77)=Chr$(27)+"[C"  'Right Arrow
Keys(79)=Chr$(27)+"[F"  'end
keys(80)=Chr$(27)+"[B"  'Left Arrow
Keys(81)=chr$(27)+"[G"  'PgDn


PRINT
PRINT "RapidQ Telnet Client -- Written By C.S.Bradford"
PRINT "Version 1.00 - Feel Free to distribute"
PRINT

IF Command$(1)="" or Command$(1)="/?" or Command$(1)="?" then

   PRINT "Command Line Usage: TEL <host or ip>
   PRINT

   END
END IF

PRINT "Connecting to "+Command$(1)+"...";
SockNum = Socket.Connect(Command$(1),23)

IF SockNum < 1 THEN GOTO TelExit

PRINT "[OK]"
PRINT

A$=""

DO                   '** MAIN LOOP
  
  A$=INKEY$

  IF A$<>"" THEN
    IF LEN(A$)=2 AND LEFT$(A$,1)=CHR$(27) THEN

       'Special Key Detected ... Translate A$ to keymap

        A$=Keys(ASC(RIGHT$(A$,1)))

    END IF
    Socket.Write(SockNum,A$,LEN(A$))
  END IF

  IF Socket.IsServerReady(SockNum) THEN
     B$=Socket.Read(SockNum,1)
     IF B$=IAC THEN
       NegotiateOptions
     ELSE
       PRINT B$;
     END IF
  END IF

  IF Socket.Transferred < 0 THEN Exit DO      'check for dropped connection

LOOP

TelExit:
  PRINT "No Connection... Exiting"
  Socket.Close


SUB NegotiateOptions

   Print "RCVD ";
   B$=""
   B$=Socket.Read(SockNum,1)

   IF B$=tWill OR b$=tWont OR b$=tDo OR b$=tDont then
      B$=B$+Socket.Read(SockNum,1)
      SELECT CASE B$
        CASE tDo+TermType
          PRINT "IAC DO TermType"
          Socket.Write(SockNum,IAC + tWill +TermType,3)
          PRINT "SENT IAC WILL TermType"

        '** add more cases here to handle other terminal options

        CASE ELSE
          IF LEFT$(B$,1)=tDo THEN                    'Reply With tWont
             PRINT "IAC ["+str$(asc(left$(b$,1)))+"]["+str$(asc(right$(b$,1)))+"]"
             Socket.Write(SockNum,IAC + tWont + Right$(b$,1),3)
             PRINT "SENT IAC WONT "+str$(asc(Right$(b$,1)))
          END IF

      END SELECT
   ELSE
      SELECT CASE B$
         CASE SB           'negotiate sub options
            WHILE RIGHT$(B$,1) <> SE
              B$=B$+Socket.Read(SockNum,1)
            WEND
            IF B$=TermTypeSB THEN
              PRINT "REQUEST FOR TermType"
              Socket.Write(SockNum, TermTypeInfo, LEN(TermTypeInfo))
              PRINT "SENT TermType INFORMATION
            ELSE
              PRINT "Unknown SUBOption... Ignoring"
            END IF

         CASE ELSE
            PRINT "Unknown Option...Ignoring"

      END SELECT
   END IF

   
END SUB
