Simple Telnet Connection / Scripting
Posted: Tue Aug 19, 2008 8:47 pm
				
				From my post here:  http://www.purebasic.fr/english/viewtopic.php?t=31106
Please don't laugh too much at this because I never worked with the network library before and certainly not with telnet in it's raw form. Take this is an example for building your own library. I didn't take the time to clean this up since it works fine for me in my application (of which it is a pretty small but important part). I use it for scripting entry of information into our old CRM system. I've tested it with two different types of "scripts" and it works fine.
The code here is stripped down but hopefully gives some idea of how to do it.
TelnetConnect() is the main procedure that connects to a server and handles initial negotiation. You can make your own mini "scripts" in a similar way to the Telnet_DoSomething(). In my program I have a queue system that will handle different types of scripts in a thread without bothering the user. It outputs what it's doing to a separate window that the user can minimize while working on other parts of the program.
TelnetWaitUntilHas() is the procedure that actually does most of the work. It's ugly, big and can certainly be better but I don't have the time to fix it up at the moment. The Send parameter contains the keys or text you want to send. The Contains parameter is what you're expecting AFTER[\b] Send is sent to the server. AsRaw tells the procedure you're sending raw character codes. For example, to send F4 to the server I connect to, I need to send "chr(27)+[P" which are the characters "27, 91 and 80" so I would do...
The code sends the F4 keystrokes as raw data and expects to be returned to the main menu.  It will watch the text sent to the client until it gets those characters.
So, yeah. This all could be used as a basis for a much, much better library. Or, later (much later ), I could work something up as a small library.
 ), I could work something up as a small library.
Hope it helps!
			Please don't laugh too much at this because I never worked with the network library before and certainly not with telnet in it's raw form. Take this is an example for building your own library. I didn't take the time to clean this up since it works fine for me in my application (of which it is a pretty small but important part). I use it for scripting entry of information into our old CRM system. I've tested it with two different types of "scripts" and it works fine.
The code here is stripped down but hopefully gives some idea of how to do it.
TelnetConnect() is the main procedure that connects to a server and handles initial negotiation. You can make your own mini "scripts" in a similar way to the Telnet_DoSomething(). In my program I have a queue system that will handle different types of scripts in a thread without bothering the user. It outputs what it's doing to a separate window that the user can minimize while working on other parts of the program.
TelnetWaitUntilHas() is the procedure that actually does most of the work. It's ugly, big and can certainly be better but I don't have the time to fix it up at the moment. The Send parameter contains the keys or text you want to send. The Contains parameter is what you're expecting AFTER[\b] Send is sent to the server. AsRaw tells the procedure you're sending raw character codes. For example, to send F4 to the server I connect to, I need to send "chr(27)+[P" which are the characters "27, 91 and 80" so I would do...
Code: Select all
TelnetWaitUntilHas(connectionid, "27,91,80", "Main Menu", #True)So, yeah. This all could be used as a basis for a much, much better library. Or, later (much later
 ), I could work something up as a small library.
 ), I could work something up as a small library.Hope it helps!
Code: Select all
;-
EnableExplicit
;-
InitNetwork()
;-
Structure s_Telnet_Result
   ;
   Text.s
   ;
EndStructure
;-
Procedure.s TelnetCleanText(Value.s)
   ;
   Protected CaughtEscape.l
   ;
   Protected *HoldChar.Character
   ;
   Protected HoldString.s = Space(Len(Value))
   ;
   Protected *HoldOut.Character = @HoldString
   ;
   Protected IndexBegin.l, IndexEnd.l
   ;
   *HoldChar = @Value
   ;
   While *HoldChar\c
      ;
      If *HoldChar\c = 27
         ;
         CaughtEscape = #True
         ;
      ElseIf CaughtEscape And (*HoldChar\c = 'm' Or *HoldChar\c = 'H')
         ;
         CaughtEscape = #False
         ;
      Else
         ;
         If CaughtEscape = #False
            ;
            *HoldOut\c = *HoldChar\c
            ;
            *HoldOut + SizeOf(Character)
            ;
         EndIf
         ;
      EndIf
      ;
      *HoldChar + SizeOf(Character)
      ;
   Wend
   ;
   ProcedureReturn Left(HoldString, *HoldOut - @HoldString)
   ;
EndProcedure
Procedure TelnetSendRaw(iConnection.l, Codes.s)
   ;
   Protected iMax.l
   Protected iLoop.l
   ;
   Protected *Memory.l
   Protected *Position.Byte
   ;
   iMax = CountString(Codes, ",") + 1
   *Memory = AllocateMemory(iMax)
   *Position = *Memory
   For iLoop = 1 To iMax
      *Position\b = Val(StringField(Codes, iLoop, ","))
      *Position + 1
   Next iLoop
   SendNetworkData(iConnection, *Memory, iMax)
   FreeMemory(*Memory)
   ;
EndProcedure
Procedure.l TelnetWaitUntilHas(iConnection.l, Send.s, Contains.s, AsRaw.l = #False, Wait.l = 10000, *Result.s_Telnet_Result = #Null)
   ;
   Protected iMax.l
   Protected iLoop.l
   ;
   Protected HoldTime.q
   Protected HoldString.s
   Protected CaughtMatch.l
   Protected *HoldChar.Character
   ;
   Protected Result.l
   ;
   Protected *Memory.l
   Protected *Position.Byte
   ;
   Protected q.l, r.l
   ;
   Protected HoldStart.l
   ;
   Protected IsError.l
   ;
   Protected HoldLength.l
   ;
   If AsRaw
      ;
      iMax = CountString(Send, ",") + 1
      *Memory = AllocateMemory(iMax)
      *Position = *Memory
      For iLoop = 1 To iMax
         *Position\b = Val(StringField(Send, iLoop, ","))
         *Position + 1
      Next iLoop
      Debug "Sending: "+Send
      SendNetworkData(iConnection, *Memory, iMax)
      Debug "Done Sending..."
      FreeMemory(*Memory) : *Memory = 0
      ;
      If Contains = "" : CaughtMatch = #True : EndIf
      ;
   Else
      ;
      For iLoop = 1 To 255
         Send = ReplaceString(Send, "chr("+Str(iLoop)+")", Chr(iLoop))
      Next iLoop
      ;
      Debug "Sending: "+Send
      If Send <> "" : SendNetworkString(iConnection, Send) : EndIf
      Debug "Done Sending..."
      ;
   EndIf
   ;
   HoldTime = ElapsedMilliseconds()
   ;
   Repeat
      ;
      If NetworkClientEvent(iConnection) = #PB_NetworkEvent_Data
         ;
         If *Memory : FreeMemory(*Memory) : *Memory = 0 : EndIf
         *Memory = AllocateMemory(1000)
         HoldLength = ReceiveNetworkData(iConnection, *Memory, 1000)
         ;
         Debug TelnetCleanText(PeekS(*Memory, HoldLength))
         ;
         If HoldLength
            ;
            *HoldChar = *Memory
            While *HoldChar - *Memory < HoldLength
               If *HoldChar\c = 0 : *HoldChar\c = 1 : EndIf
               *HoldChar + SizeOf(Character)
            Wend
            ;
            HoldString = HoldString + TelnetCleanText(ReplaceString(PeekS(*Memory, HoldLength), Chr(1), ""))
            ;
            If Contains <> ""
               ;
               iMax = CountString(Contains, Chr(29)) + 1
               For iLoop = 1 To iMax
                  If FindString(HoldString, StringField(Contains, iLoop, Chr(29)), 1)
                     CaughtMatch = #True
                     If iLoop > 1 : IsError = #True : EndIf
                  EndIf
               Next iLoop
               ;
            Else
               ;CaughtMatch = #True
            EndIf
            ;
         EndIf
         ;
         If *Memory : FreeMemory(*Memory) : *Memory = 0 : EndIf
         ;
      EndIf
      ;
   Until CaughtMatch Or ElapsedMilliseconds() - HoldTime > Wait
   ;
   Debug "       Took: "+Str(ElapsedMilliseconds() - HoldTime)
   ;
   If *Result : *Result\Text = HoldString : EndIf
   ;
   If Contains = "" : CaughtMatch = #True : EndIf
   ;
   If IsError : CaughtMatch = #False : EndIf
   ; 
   ProcedureReturn CaughtMatch
   ;
EndProcedure
Procedure Telnet_DoSomething(iConnection.l)
   ;
   If TelnetWaitUntilHas(iConnection, "D", "A. Inventory Information")
      ;
      ;
   EndIf
   ;
EndProcedure
Procedure TelnetConnect()
   ;
   Protected *HoldOut.l
   Protected *Out.Byte
   ;
   Protected *HoldMemory.l
   ;
   Protected iLoop.l
   ;
   Protected CaughtMatch.l
   ;
   Protected GotPrompt.l, GotPassword.l
   ;
   Protected HasDBChoiceMenu.l, HasMainMenu.l
   ;
   Protected *HoldChar.Character
   ;
   Protected StartTime.q
   ;
   Protected HoldString.s
   ;
   Protected HoldValue.l
   ;
   Protected HoldThread
   ;
   Protected Result.l
   ;
   Protected IsCheckingTerminalType.l
   ;
   Protected iConnection.l
   ;
   Protected *Memory.l
   Protected HoldLength.l
   ;
   Protected HoldFlash.FLASHWINFO
   ;
   Protected HoldResult.s_Telnet_Result
   ;
   Protected lResult.l
   ;
   iConnection = OpenNetworkConnection("server_name", 23)
   ;
   If iConnection
      ;
      ;{ Negotiate
      Repeat
         ;
         Result = 0
         ;
         If *Memory : FreeMemory(*Memory) : *Memory = 0 : EndIf
         ;
         *Memory = AllocateMemory(1000)
         ;
         HoldLength = ReceiveNetworkData(iConnection, *Memory, 1000)
         ;
         If HoldLength = 0 : Break : EndIf
         ;
         *HoldMemory = *Memory
         ;
         ; Debug Str(gLength)+" characters."
         ; For iLoop = 0 To gLength - 1
            ; Debug Str(PeekB(*HoldMemory + iLoop) & $FF)
         ; Next iLoop
         ;
         While *HoldMemory - *Memory < HoldLength
            ;
            If PeekB(*HoldMemory) & $FF  = 255
               ; IAC code.
               HoldValue = PeekB(*HoldMemory + 1) & $FF 
               ;
               If HoldValue = 250
                  ;{ SB
                  If PeekB(*HoldMemory + 2) & $FF = 24 And PeekB(*HoldMemory + 3) & $FF = 1 And PeekB(*HoldMemory + 4) & $FF = 255 And PeekB(*HoldMemory + 5) & $FF = 240
                     ; IAC SB TERMINAL-TYPE SEND IAC SE
                     ; TelnetSendRawData("255,250,24,0,65,78,83,73,255,240")
                     TelnetSendRaw(iConnection, "255,250,24,0,86,84,49,48,48,255,240")
                     ; Send ANSI terminal type.
                     *HoldMemory + 6 
                     ;
                  EndIf
                  ;}
               ElseIf HoldValue = 251
                  ;{ Will
                  If PeekB(*HoldMemory + 2) & $FF = 1
                     ; Echo
                     TelnetSendRaw(iConnection, "255,254,1")
                     ; Send DONT echo.
                     *HoldMemory + 3
                     ;
                  ElseIf PeekB(*HoldMemory + 2) & $FF = 3
                     ; Echo
                     TelnetSendRaw(iConnection, "255,253,3")
                     ; Send DON suppress go ahead.
                     *HoldMemory + 3
                     ;
                  Else
                     ;
                     TelnetSendRaw(iConnection, "255,254,"+Str(PeekB(*HoldMemory+2) & $FF))
                     ; Send DONT to the message.
                     *HoldMemory + 3
                     ; 
                  EndIf
                  ;}
               ElseIf HoldValue = 252
                  ;{ Won't
                  If PeekB(*HoldMemory + 2) & $FF = 37
                     ; Athenticate
                     TelnetSendRaw(iConnection, "255,254,37")
                     ; Send DONT authenticate.
                     *HoldMemory + 3
                     ;
                  Else
                     ;
                     TelnetSendRaw(iConnection, "255,254,"+Str(PeekB(*HoldMemory+2) & $FF))
                     ; Send DONT to the message.
                     *HoldMemory + 3
                     ;
                  EndIf
                  ;}
               ElseIf HoldValue = 253
                  ;{ Do
                  If PeekB(*HoldMemory + 2) & $FF = 24
                     ; Terminal Type
                     If IsCheckingTerminalType = #False : TelnetSendRaw(iConnection, "255,251,24") : EndIf
                     ; If IsCheckingTerminalType = #False : TelnetSendRaw(iconnection, "255,252,24") : EndIf
                     ; Agree to send terminal type.  Don't send this out again if already sent.
                     *HoldMemory + 3
                     ;
                     IsCheckingTerminalType = #True
                     ;
                  ElseIf PeekB(*HoldMemory + 2) & $FF = 1
                     ; Allow echo
                     TelnetSendRaw(iConnection, "255,252,1")
                     ; Tell the server we WONT echo.
                     *HoldMemory + 3
                     ;
                  ElseIf PeekB(*HoldMemory + 2) & $FF = 3
                     ; SUPPRESS-GO-AHEAD, Suppress Go Ahead.
                     TelnetSendRaw(iConnection, "255,251,3")
                     ; Tell the server we WILL suppress go-ahead messages
                     *HoldMemory + 3
                     ;
                  ElseIf PeekB(*HoldMemory + 2) & $FF = 31
                     ; Negotiate about window size
                     TelnetSendRaw(iConnection, "255,252,31")
                     ; Tell the server we WONT negotiate about window size.
                     *HoldMemory + 3
                     ;
                  Else 
                     ;
                     TelnetSendRaw(iConnection, "255,252,"+Str(PeekB(*HoldMemory+2) & $FF))
                     ; Send WONT to the message.
                     *HoldMemory + 3
                     ;
                  EndIf
                  ;}
               ElseIf HoldValue = 254
                  ;{ Don't
                  If PeekB(*HoldMemory + 2) & $FF = 37
                     ; Athenticate
                     TelnetSendRaw(iConnection, "255,252,37")
                     ; Send WONT authenticate.
                     *HoldMemory + 3
                     ;
                  Else
                     ;
                     TelnetSendRaw(iConnection, "255,252,"+Str(PeekB(*HoldMemory+2) & $FF))
                     ; Send WONT to the message.
                     *HoldMemory + 3
                  EndIf
                  ;}
               EndIf
               ;
            Else
               ;
               If PeekB(*HoldMemory) & $FF  <= 0 : Break 2 : EndIf
               ;
               *HoldMemory + 1
               ;
            EndIf
            ;
         Wend
         ;
      ForEver
      ; 
      If *HoldOut : FreeMemory(*HoldOut) : *HoldOut = 0 : EndIf
      ;
      If *Memory : FreeMemory(*Memory) : *Memory = 0 : EndIf
      ;}
      ;
      If TelnetWaitUntilHas(iConnection, "", "login:")
         ;
         If TelnetWaitUntilHas(iConnection, "username_here" + Chr(13) + Chr(10), "Password:")
            ;
            lResult = TelnetWaitUntilHas(iConnection, "password_here" + Chr(13) + Chr(10), "Secur Menu"+Chr(29)+"You entered an invalid login name Or Password.", #False, 10000, @HoldResult)
            ;
            If lResult = 0
               ; Bad username or password.
               Debug "Bad username or password."
               ;
            Else
               ; Password is valid.
               If TelnetWaitUntilHas(iConnection, Chr(13)+Chr(10), "A. Overview")
                  ;
                  Telnet_DoSomething(iConnection)
                  ;- Do work here.  The user is at the main menu.
                  ;
                  If TelnetWaitUntilHas(iConnection, "27,91,80", "Do you want to Exit?", #True)
                     ;
                     Debug "Gracefully exited."
                     ;
                  EndIf
                  ;
               EndIf
               ;
            EndIf
            ;
         EndIf
         ; Session.Send chr(27) + "~[P"
         ; and then "Y" + enter
         ; and then Session.Send chr(27) + "~[P"
      EndIf
      ;
      CloseNetworkConnection(iConnection) : iConnection = 0
      ;
   EndIf
   ;
EndProcedure
;-
TelnetConnect()
;-
End
;-